fix(combo): ignore benign empty error fields in streaming quality validation (502 false-positive on opencode tool calls) - #9786
Merged
diegosouzapw merged 1 commit intoAug 9, 2026
Conversation
…idation
isStreamingUpstreamError used a key-presence check (parsed.error != null)
which false-positives on benign values some backends emit on every chunk
({}, '', false, 0). When opencode issues a tool-call turn, the upstream SSE
opens with role-only frames (no recognized content) and a later chunk that
carries real tool_calls content PLUS a benign empty error field. The error
gate runs BEFORE content recognizers, so that single frame short-circuits
to 'error' -> 502 'streaming upstream error'. Same combo via kilocode works
because its wire format never emits the empty error field.
Fix: isSubstantiveError() helper — only treat error as real when it carries
non-empty string, non-empty object, or explicit true. Empty object {}, empty
string '', false, and 0 are benign.
TDD: tests/unit/quality-validation-benign-error.test.ts proves tool_calls
chunk with error:{} or error:'' is valid (was 502), while a real error
{message, code} still correctly fails.
Owner
Author
|
Thanks for the contribution. PR looks structurally viable from diff-level metadata. Please confirm gates with /merge-prs flow before merge. |
2 similar comments
Owner
Author
|
Thanks for the contribution. PR looks structurally viable from diff-level metadata. Please confirm gates with /merge-prs flow before merge. |
Owner
Author
|
Thanks for the contribution. PR looks structurally viable from diff-level metadata. Please confirm gates with /merge-prs flow before merge. |
muhamadgalihsaputra
pushed a commit
to niyatna/NiyatnaRoute
that referenced
this pull request
Sep 27, 2026
…quality-validation-benign-error fix(combo): ignore benign empty error fields in streaming quality validation (502 false-positive on opencode tool calls)
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.
Causa-raiz
A qualidade de streaming (
validateResponseQuality→isStreamingUpstreamErroremopen-sse/services/combo/validateQuality.ts) usava uma checagem de presença de chave em vez de conteúdo:parsed.error != nullé true para{},"",false,0— qualquer valor excetonull/undefined. Quando um cliente como o opencode faz uma chamada de tool call, o upstream SSE abre com frames de role-only (sem conteúdo reconhecido) e um chunk posterior que carrega tool_calls reais mais um campoerrorvazio benigno (que alguns backends emitem em todo chunk). O gate de erro roda ANTES dos reconhecedores de conteúdo, então esse único frame vira "error" → 502 "streaming upstream error".O mesmo combo via kilocode funciona porque o wire format do kilocode não emite o campo
errorvazio → o gate nunca dispara. Por isso o bug parece tool-call-specific e client-specific.Fix
Novo helper
isSubstantiveError(): só trataerrorcomo falha real quando tem conteúdo substantivo:{message, code}) → errotrueexplícito → erro{},"",false,0→ benigno (não erro)Aplicado ao gate de top-level (
parsed.error). O caso nested (parsed.response.status === "failed") permanece intacto por já ser guardado pelo status.TDD
tests/unit/quality-validation-benign-error.test.ts— 3 casos:error:{}→ valid (antes: inválido/502)error:""→ valid (antes: inválido/502){message, code}→ invalid (garante que erro verdadeiro ainda falha)streaming-empty-content-block,combo-quality-validator-reasoning,combo-responses-sse-failure-fallback)combo-routing-engine+ typecheck em validaçãoEscopo
Toca apenas
open-sse/services/combo/validateQuality.ts(1 helper novo + 1 linha) + 1 teste novo.