fix(decopilot): sanitize non-Error stream errors instead of leaking raw JSON#3946
Merged
Merged
Conversation
…aw JSON
Gateway/provider stream errors often arrive as plain objects rather than
`Error` instances — e.g. the `{ code, message, metadata }` shape a 504
"Upstream idle timeout exceeded" carries. Two spots mishandled this:
- `sanitizeStreamError` only sanitized `Error` instances and otherwise fell
back to `JSON.stringify`, so the user-facing / persisted error text became
the raw `{"code":504,...}` blob and the provider-URL/branding stripping was
bypassed entirely. Now extract `message` + a numeric `statusCode`/`code`
from object errors and run them through the same credits + strip logic.
- `stringifyProviderError` logged `[object Object]` for object errors (the
`[runAgentLoop:...] Error` line), hiding the real cause. Now prefers a
string `message` field, falling back to JSON.
Add unit tests covering the 504-object path for both sanitize and classify.
decocms Bot
pushed a commit
that referenced
this pull request
Jun 16, 2026
PR: #3946 fix(decopilot): sanitize non-Error stream errors instead of leaking raw JSON Bump type: patch - @decocms/harness (packages/harness/package.json): 1.6.0 -> 1.6.1
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
Production logs surfaced a decopilot run failing on an upstream
504 Upstream idle timeout exceeded. Tracing the handling turned up two defects in how non-Errorstream errors are processed. Gateway/provider errors frequently arrive as plain objects ({ code, message, metadata }), notErrorinstances, and both code paths assumedinstanceof Error:sanitizeStreamErroronly sanitizedErrorinstances; everything else fell through toJSON.stringify. So the message persisted to the thread and shown to the user became the literal{"code":504,"message":"Upstream idle timeout exceeded","metadata":{"error_type":"timeout"}}blob, and the provider-URL/branding stripping was skipped entirely.stringifyProviderErrordid`${error}`for object errors, so the[runAgentLoop:...] Errorline logged[object Object]and hid the real cause.Changes
stream-error.ts: addextractErrorParts(error)that pulls a stringmessageand a numericstatusCode(falling back to a numericcode) out of object errors.sanitizeStreamErrornow runs bothErrorand object errors through the same credits-detection + URL/branding-strip logic.native-agent-loop-core.ts:stringifyProviderErrornow prefers a stringmessagefield on object errors, falling back to JSON.stream-error.test.tscovering the 504-object path for bothsanitizeStreamErrorandclassifyStreamError, plus the credits/opaque-object branches.Scope / not included
This does not change retry/recovery behavior. The underlying resilience gap — a mid-stream idle timeout has no auto-retry or resume, since the SDK's
maxRetriesonly covers the initial request — is intentionally left out as a separate design discussion.Testing
bun test packages/harness/src/decopilot/stream-error.test.ts native-agent-loop-core.test.ts— passtsc --noEmitonpackages/harness— cleanbun run fmt— appliedSummary by cubic
Sanitizes non-Error stream errors so users see clean messages instead of raw JSON. Also fixes provider error logging to show real messages from object errors.
Errorand object errors: extractmessage+statusCode/code, strip provider URLs/branding, and tag credit issues with[CREDITS].messageinstringifyProviderError; fall back to JSON to avoid[object Object].Written for commit 4669007. Summary will update on new commits.