Skip to content

Commit b840a3a

Browse files
committed
fix(chat): detect aborts by signal state, not error identity
fetch rejects with the RAW abort reason when its signal carries one — abort('unmount:client_cleanup') surfaces as a plain string, so every err.name === 'AbortError' check missed it and the restore path never ran (verified live). The test stub now rejects with the raw reason like real fetch, which turns this gap red.
1 parent 04147f7 commit b840a3a

2 files changed

Lines changed: 10 additions & 9 deletions

File tree

apps/sim/app/workspace/[workspaceId]/home/hooks/use-chat.mount-send.test.tsx

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,6 @@ function emptySseResponse(): Response {
5151
return new Response(stream, { status: 200, headers: { 'Content-Type': 'text/event-stream' } })
5252
}
5353

54-
function abortError(): Error {
55-
const error = new Error('Aborted')
56-
error.name = 'AbortError'
57-
return error
58-
}
59-
6054
async function fetchStub(input: RequestInfo | URL, init?: RequestInit): Promise<Response> {
6155
const url = String(input instanceof Request ? input.url : input)
6256

@@ -66,11 +60,13 @@ async function fetchStub(input: RequestInfo | URL, init?: RequestInit): Promise<
6660
return new Promise<Response>((_, reject) => {
6761
const signal = init?.signal
6862
if (!signal) return
63+
// Real fetch rejects with the RAW abort reason (a string here), not an
64+
// AbortError — the regression this suite guards depends on that shape.
6965
if (signal.aborted) {
70-
reject(abortError())
66+
reject(signal.reason)
7167
return
7268
}
73-
signal.addEventListener('abort', () => reject(abortError()), { once: true })
69+
signal.addEventListener('abort', () => reject(signal.reason), { once: true })
7470
})
7571
}
7672

apps/sim/app/workspace/[workspaceId]/home/hooks/use-chat.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3833,7 +3833,12 @@ export function useChat(
38333833
}
38343834
}
38353835
} catch (err) {
3836-
if (err instanceof Error && err.name === 'AbortError') {
3836+
/* fetch rejects with the RAW abort reason (here a plain string) when
3837+
its signal was aborted with abort(reason) — an `err.name` check alone
3838+
misses those, so abort detection also consults the signal itself. */
3839+
const sendWasAborted =
3840+
(err instanceof Error && err.name === 'AbortError') || sendAbortSignal?.aborted === true
3841+
if (sendWasAborted) {
38373842
if (sendAbortSignal?.reason === 'unmount:client_cleanup' && !sendReachedServer) {
38383843
/* The mount-settling effect cycle (Suspense hide/reveal) ran the
38393844
unmount cleanup while this send was still pre-dispatch. Nothing

0 commit comments

Comments
 (0)