Uh oh!
There was an error while loading. Please reload this page.
fix(chat): stop chats storing a resource they can never send with - #6344
Conversation
A chat resource persisted with a blank id made every later message fail: the write contract accepted `id: ''` while the send schema required `min(1)`, so the request 400d before a stream existed and the client's reconnect 404d. The tab could not be removed either, since the delete route requires a non-empty id. Twelve production chats were in this state. The id came from an agent-written file chip that carried only a filename: the client filled the missing id with `''` when the file was absent from its list, which it always is for a file the agent just created. - model the unresolved state (`WorkspaceResourceRef`) instead of faking an id, and resolve chip refs at one choke point that may refuse - close the stale-cache race by fetching the file list before giving up, so clicking a just-created file opens it instead of doing nothing - reject blank ids at the stream, write and send boundaries, and drop them wherever stored resources are read, which self-heals affected chats - collapse the 5-6 duplicate POSTs every resource add was firing - log rejected chat bodies, which previously left no trace at all
The latest updates on your projects. Learn more about Vercel for GitHub. |
PR SummaryMedium Risk Overview Modeling and resolution: Introduces Defense in depth: Related UI fixes: Reviewed by Cursor Bugbot for commit 6bea695. Configure here. |
Greptile SummaryThe PR prevents unresolved or blank-ID chat resources from being persisted or sent, sanitizes existing resource lists, and resolves file chips against a refreshed workspace-file list.
Confidence Score: 5/5The PR appears safe to merge. No blocking failure remains.
|
| Filename | Overview |
|---|---|
| apps/sim/app/workspace/[workspaceId]/home/resolve-resource-ref.ts | Adds centralized resolution of lossy file references while refusing ambiguous or unaddressable resources. |
| apps/sim/app/workspace/[workspaceId]/home/home.tsx | Resolves resource chips against cached files, retries after a forced refresh, and reports unresolved selections. |
| apps/sim/app/workspace/[workspaceId]/home/hooks/use-chat.ts | Rejects unaddressable tabs, sanitizes restored resources, and deduplicates concurrent persistence requests. |
| apps/sim/lib/copilot/resources/types.ts | Defines unresolved resource references and centralizes addressability, canonicalization, and sanitization. |
| apps/sim/lib/copilot/chat/post.ts | Drops blank-ID attachments before validation, sanitizes newly persisted resources, and logs rejected requests. |
| apps/sim/lib/copilot/resources/persistence.ts | Sanitizes resource lists during persistence and removal so legacy blank-ID records self-heal. |
| apps/sim/lib/copilot/request/session/contract.ts | Rejects stream resource events whose identifiers are blank or whitespace-only. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart LR
Chip[Message resource chip] --> Ref[WorkspaceResourceRef]
Ref --> Resolve[Resolve against cached workspace files]
Resolve -->|Found| Open[Open addressable resource]
Resolve -->|Missing file| Refresh[Refetch workspace files]
Refresh -->|Found| Open
Refresh -->|Still missing| Refuse[Show error and refuse tab creation]
Open --> Add[Deduplicated resource add]
Add --> Persist[Sanitize and persist]
Persist --> Send[Sanitize and send chat request]
Reviews (4): Last reviewed commit: "fix(chat): do not report an unreachable ..." | Re-trigger Greptile
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
A rendered link collapses a resource's id and path into one href, so the click handler cannot tell them apart. Classifying on a separator got a bare filename in `path` wrong, and the resolver then trusted it as an id — opening and persisting a tab pointing at nothing. Drop the classifier and let the resolver try each candidate as an id, a VFS path and a unique name. A file ref must now match a record the workspace actually has; the stale-list case is covered by the refetch, so an id that never resolves was never an id.
waleedlatif1
commented
Aug 6, 2026
waleedlatif1
commented
Aug 6, 2026
@cursor review |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 9b69b03. Configure here.
The chip renders as a button with a hover state, so refusing to open it silently reads as a broken control. Say what happened instead.
waleedlatif1
commented
Aug 6, 2026
waleedlatif1
commented
Aug 6, 2026
@cursor review |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 791c098. Configure here.
A failed refetch and a successful one that found nothing were both collapsed to an empty list, so a network blip told the user the file does not exist. Keep the two apart and say which happened.
waleedlatif1
commented
Aug 6, 2026
waleedlatif1
commented
Aug 6, 2026
@cursor review |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 6bea695. Configure here.
Summary
id: ""made every later message fail. The write contract acceptedz.string()while the send schema required.min(1), soPOST /api/mothership/chat400d before a stream existed and the client's reconnect 404d — the "stream death" symptom. The tab couldn't be removed either, since the delete route requires a non-empty id. 12 production chats are in this state (staging logs +copilot_chats.resourcesconfirm it).''when the file wasn't in its cached list — which it never is for a file the agent just created. Prod logs show the same file added withid: "", then 4s later with its real id.WorkspaceResourceRef) instead of faking an id, and resolve chip refs at one choke point that is allowed to refuse.POST /chat/resourcesevery resource add was firing.Type of Change
Testing
tscclean,check:api-validation+check:react-querypass, 1571 copilot/API + 1787 workspace + 275 hooks tests pass. New unit tests cover ref resolution (incl. ambiguous-name refusal), the blank-id stream payload, and the sanitizer; each was verified to fail without its fix. Not yet exercised in a browser.Notes for review
wsreslink id/path classification. Both are in the same subsystem and were found while tracing it.Checklist