Repository navigation
Description
Provide environment information
System:
OS: macOS 27.0.1
CPU: (12) arm64 Apple M4 Pro
Memory: 24.00 GB
Shell: 5.9 - zsh
Binaries:
Node: 24.12.0
npm: 11.6.2
pnpm: 10.33.0
bun: 1.3.7
Affected client (end user's browser):
Safari 17.2.1 on macOS (Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.2.1 Safari/605.1.15)
Packages:
@trigger.dev/sdk 4.8.3 (also reproduced on 4.7.3)
ai 7.0.162
next 16 (App Router), React 19
Describe the bug
The browser-side chat transport (useTriggerChatTransport from @trigger.dev/sdk/chat/react) calls AbortSignal.any without checking that it exists. Safari only added AbortSignal.any in 17.4, so on Safari 17.0–17.3 (and 16.x) every chat request throws:
TypeError: AbortSignal.any is not a function. (In 'AbortSignal.any([t,o.signal])', 'AbortSignal.any' is undefined)
at subscribeToSessionStream
The AI SDK always passes an abortSignal to sendMessages / reconnectToStream, so the AbortSignal.any branch is hit on every send and every resume. Chat is unusable for these users.
Next.js supports Safari 16.4+ by default, so these browsers are still in the supported range for most apps using the SDK.
Unguarded call sites in 4.8.3:
- dist/esm/v3/chat.js:614 (reconnectToStream)
- dist/esm/v3/chat.js:1210 (subscribeToSessionStream)
- dist/esm/v3/chat-client.js:571
(and the matching commonjs builds)
Reproduction repo
No standalone repo. Any app that uses useTriggerChatTransport with useChat reproduces it on Safari < 17.4, or on any browser after removing the method (see steps below).
To reproduce
- Set up a chat using
useTriggerChatTransportfrom@trigger.dev/sdk/chat/reactwithuseChatfrom@ai-sdk/react. - Open the app in Safari 17.2 (or any Safari < 17.4).
Alternatively, in any modern browser, rundelete AbortSignal.anyin the devtools console before the chat mounts. - Send a message, or reload a page with an active chat so it resumes the stream.
- The transport throws
TypeError: AbortSignal.any is not a functionand the chat request fails.
Suggested fix
v3/sessions.js already guards this for Node 18 (if (typeof AbortSignal.any === "function") ... else { wire the caller signal manually }). Applying the same guard, or a small shared helper, to the chat transport would fix it. For example:
function anySignal(signals: AbortSignal[]): AbortSignal {
if (typeof AbortSignal.any === "function") return AbortSignal.any(signals);
const controller = new AbortController();
const aborted = signals.find((s) => s.aborted);
if (aborted) {
controller.abort(aborted.reason);
return controller.signal;
}
for (const s of signals) {
s.addEventListener("abort", () => controller.abort(s.reason), {
once: true,
signal: controller.signal,
});
}
return controller.signal;
}
and use it at the three call sites in chat.js and chat-client.js.
Additional information
We've worked around it on our side by polyfilling AbortSignal.any in instrumentation-client.ts. It would still be good to fix in the SDK, since apps using the chat transport won't know they need the polyfill until older-Safari users report broken chat.
Activity