fix(streaming): mark the research span as ERROR when the stream fails - #930
Merged
Conversation
Provider failures on the chat stream are logged and serialized to the client, but the Langfuse root observation still ends with level DEFAULT and no status message. Langfuse is the only surface that covers guest executions, so a provider outage leaves no error-level observation on the research path at all. Capture the error from the stream onError callback and the surrounding catch, then mark the root span with level ERROR and a status message before it is ended. The status message is built only from the public error payload's code and message, which is already sent to every client, so no user content or provider response body reaches the trace.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
When the chat stream fails, the failure never reaches the tracing surface. The root
researchobservation ends withlevel: DEFAULTandstatusMessage: nulleven though the turn produced no answer at all.This matters because tracing is the only surface that covers guest and ephemeral executions, and it is what gets scanned for error-level observations. A recent provider outage produced a large number of consecutive turns where every observation on the research path was recorded at
DEFAULTwith zero tokens and no output. The failure was only visible becausetitle-generationruns throughgenerateText, which throws and therefore does get recorded as an error. Had the chat path been the only caller, the outage would have produced no error-level observation whatsoever.Root cause
The provider error arrives as an error chunk inside the response stream rather than as a thrown exception, so the surrounding
try/catchnever runs and the request completes with HTTP 200. TheonErrorcallback passed totoUIMessageStreamResponsedoes fire, and it logs the error and serializes it for the client, but nothing marks the span.endTracing()then callsrootSpan.end()on an unmodified span.Fix
Capture the error in both entry points (
create-chat-stream-response.tsandcreate-ephemeral-chat-stream-response.ts), from the streamonErrorcallback and from the surroundingcatch, and mark the root span withlevel: 'ERROR'plus a status message before it is ended.The status message comes from a new
describeStreamErrorhelper that uses only thecodeanderrorfields of the existing public error payload. That payload is already sent to every client, so the trace cannot pick up user content, a provider response body, or credentials. The result is capped at 300 characters, which matters because some public payloads are passed through verbatim.This is additive only. Response status codes, the serialized public error payload, and the existing logging are unchanged.
Client aborts are not affected: aborts travel as a distinct
abortchunk and setisAbortedononFinish, so they never reachonErrorand will not be marked as errors.Verification
Error, a non-Error value, and the length cap, asserting the exact resulting message and that raw input text never appears in it.bun run test295 passed,bun typecheck,bun lint,bun format:checkandbun run buildall pass.provider_billing: The AI service is currently unavailable.