Skip to content

Commit 623b3e9

Browse files
icecrasher321claude
andcommitted
fix(execution): recognise Firefox's NetworkError form as a stream drop
The transport-failure matcher only knew Chrome's "network error" with a space, so Firefox's "NetworkError when attempting to fetch resource." fell through as a plain failure. Now that every live stream shares this classifier, match the browsers' known messages as patterns and cover each form in the executor test. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
1 parent f431355 commit 623b3e9

2 files changed

Lines changed: 43 additions & 25 deletions

File tree

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/utils/workflow-execution-utils.test.ts

Lines changed: 30 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -96,29 +96,37 @@ describe('workflow-execution-utils', () => {
9696
return store
9797
}
9898

99-
it('classifies a transport drop after the server acknowledged the run as an interruption', async () => {
100-
/*
101-
* The Chat run tool only preserves a run for reconnect when it sees
102-
* SSEStreamInterruptedError; a raw TypeError from the body reader used to
103-
* fall through as a plain failure, reporting an error to Sim and tearing
104-
* the run down while the server kept executing it.
105-
*/
106-
const store = stubExecutionStore()
107-
stubAcknowledgedStream(new TypeError('network error'))
108-
109-
const promise = executeWorkflowWithFullLogging({
110-
workflowId: 'wf-1',
111-
executionId: 'exec-1',
112-
copilotToolCallId: 'tool-1',
113-
preserveExecutionOnTerminal: true,
114-
})
99+
it.each([
100+
['Chrome', 'network error'],
101+
['Chrome before headers', 'Failed to fetch'],
102+
['Firefox', 'NetworkError when attempting to fetch resource.'],
103+
['Safari', 'Load failed'],
104+
])(
105+
'classifies a %s transport drop after the server acknowledged the run as an interruption',
106+
async (_browser, message) => {
107+
/*
108+
* The Chat run tool only preserves a run for reconnect when it sees
109+
* SSEStreamInterruptedError; a raw TypeError from the body reader used to
110+
* fall through as a plain failure, reporting an error to Sim and tearing
111+
* the run down while the server kept executing it.
112+
*/
113+
const store = stubExecutionStore()
114+
stubAcknowledgedStream(new TypeError(message))
115+
116+
const promise = executeWorkflowWithFullLogging({
117+
workflowId: 'wf-1',
118+
executionId: 'exec-1',
119+
copilotToolCallId: 'tool-1',
120+
preserveExecutionOnTerminal: true,
121+
})
115122

116-
await expect(promise).rejects.toBeInstanceOf(SSEStreamInterruptedError)
117-
await expect(promise).rejects.toMatchObject({ executionId: 'exec-server' })
118-
expect(store.setCurrentExecutionId).toHaveBeenCalledWith('wf-1', 'exec-server')
119-
expect(store.setCurrentExecutionId).not.toHaveBeenCalledWith('wf-1', null)
120-
expect(store.setIsExecuting).not.toHaveBeenCalled()
121-
})
123+
await expect(promise).rejects.toBeInstanceOf(SSEStreamInterruptedError)
124+
await expect(promise).rejects.toMatchObject({ executionId: 'exec-server' })
125+
expect(store.setCurrentExecutionId).toHaveBeenCalledWith('wf-1', 'exec-server')
126+
expect(store.setCurrentExecutionId).not.toHaveBeenCalledWith('wf-1', null)
127+
expect(store.setIsExecuting).not.toHaveBeenCalled()
128+
}
129+
)
122130

123131
it.each([
124132
['a client abort', new DOMException('Aborted', 'AbortError')],

apps/sim/hooks/use-execution-stream.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,12 +71,22 @@ function isClientDisconnectError(error: any): boolean {
7171
return error.name === 'AbortError'
7272
}
7373

74+
/**
75+
* Messages browsers put on the TypeError a fetch or body read rejects with when
76+
* the connection drops: Chrome's "network error" and "Failed to fetch",
77+
* Firefox's "NetworkError when attempting to fetch resource.", and Safari's
78+
* "Load failed".
79+
*/
80+
const TRANSPORT_FAILURE_MESSAGE_PATTERNS = [
81+
/network\s?error/,
82+
/failed to fetch/,
83+
/load failed/,
84+
] as const
85+
7486
function isRecoverableStreamError(error: any): boolean {
7587
if (isClientDisconnectError(error)) return false
7688
const msg = (error.message ?? '').toLowerCase()
77-
return (
78-
msg.includes('network error') || msg.includes('failed to fetch') || msg.includes('load failed')
79-
)
89+
return TRANSPORT_FAILURE_MESSAGE_PATTERNS.some((pattern) => pattern.test(msg))
8090
}
8191

8292
/**

0 commit comments

Comments
 (0)