fix: pre-empt agent library stream error throws with sanitized finalize code#347
Draft
fix: pre-empt agent library stream error throws with sanitized finalize code#347
Conversation
…ze code @convex-dev/agent@0.6.1 processes saveStreamDeltas events in its internal finalizeMessage mutation using the AI SDK stream processor. When a provider error event (e.g. Gemini high-demand 503) arrives in those deltas, the mutation throws the raw provider error text. Because this mutation runs in a Convex V8 isolate independently from the calling action, Convex reports the throw to Sentry before any action-level catch handler can intercept it, creating noise for every transient provider overload. Fix: add an onError callback to thread.streamText() in attemptStream that pre-emptively calls safeFinalizePending with the sanitized error code before result.text rejects. If our finalization runs first, the agent library finds the message already in "failed" state and skips delta processing, preventing the raw provider error from reaching the V8 isolate throw path. Also extract the error-reporting cluster to resilienceReporting.ts to keep resilience.ts under the 400-line hard cap. https://claude.ai/code/session_01CJ3Ax6zGpqhHdFU36dwaLx
Contributor
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
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.
Summary
@convex-dev/agent@0.6.1stores stream events viasaveStreamDeltasand processes them in its internalfinalizeMessageV8 mutation using the AI SDK'sprocess-ui-message-stream.ts. When a provider error event arrives in those stored deltas (e.g. a Gemini "high demand" 503), the mutation throws the raw provider error text. Because this mutation runs in a Convex V8 isolate independently from the calling action, Convex's built-in Sentry integration reports the throw before any action-level.catch()can intercept it — creating noise for every transient provider overload.onErrorcallback tothread.streamText()inattemptStreamthat pre-emptively callssafeFinalizePendingwith the sanitized error code (e.g."provider_overload") beforeresult.textrejects. If our finalization runs first, the agent library'sfinalizeMessagefinds the message already in"failed"state and skips delta processing — preventing the raw error from reaching the V8 throw path.finalizePendingMessages,reportError,tryReportByok, and their safe wrappers +getFinalizeCodeForError) was extracted to a newresilienceReporting.tsto keepresilience.tsunder the 400-line hard cap.Why a proper fix, not a suppression
Prior attempts added the Gemini "high demand" substring to
sentryBeforeSend.ts(Next.js Sentry filter), but that only filters the client-side throw. The V8 mutation throw goes through Convex's separate Sentry integration, which bypassesbeforeSendentirely. The only correct fix is to prevent the throw from happening in the first place.Test plan
convex/ai/resilienceStreamFailure.test.ts— 4 new tests added to"onError pre-emptive finalization"describe block:onErrorfires with Gemini high-demand →finalizeMessagecalled with"provider_overload"(not raw text)onErrorfires with Claude overload → same"provider_overload"codeonErrorfired) → no spuriousfinalizeMessagecalls./node_modules/.bin/vitest run --project backend)tsc --noEmit)https://claude.ai/code/session_01CJ3Ax6zGpqhHdFU36dwaLx
Generated by Claude Code