Skip to content

fix(agent): surface opencode StructuredOutputError instead of text-parsing prose#375

Merged
kunchenguid merged 2 commits into
kunchenguid:mainfrom
gfiorav:fm/nm-opencode-parse-r3
Jul 3, 2026
Merged

fix(agent): surface opencode StructuredOutputError instead of text-parsing prose#375
kunchenguid merged 2 commits into
kunchenguid:mainfrom
gfiorav:fm/nm-opencode-parse-r3

Conversation

@gfiorav

@gfiorav gfiorav commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Intent

The developer was tasked with fixing a bug in the no-mistakes CLI where the opencode agent integration failed parsing with 'invalid character N looking for beginning of value' because it tried to text-parse reasoning prose when opencode 1.17.12 reports a structured-output failure via info.error. They investigated the code path under internal/agent, identified the root cause (no-mistakes reads info.structured but ignores info.error containing StructuredOutputError, and uses retryCount=1 instead of opencode's default 2), wrote a scout report, then implemented a promoted ship fix: decode info.error in opencode_types.go, surface StructuredOutputError in opencode.go before the text-parse fallback, bump retryCount to 2, and add a regression test TestOpencodeAgent_StructuredOutputError. The fix was committed on branch fm/nm-opencode-parse-r3 with clean gofmt/lint/race tests, and they drove it through the no-mistakes validation pipeline where review/test/document/lint all passed but push initially failed with a 403 due to wrong git credentials, prompting a re-run after fork configuration. The final transcript shows the rerun in progress.

What Changed

  • Decode info.error from opencode responses in opencode_types.go so StructuredOutputError payloads are recognized alongside the existing info.structured check.
  • Surface StructuredOutputError in the opencode agent runner (opencode.go) before the text-parsing reasoning-prose fallback, and align retryCount with opencode's default of 2 (opencode_http.go).
  • Add a TestOpencodeAgent_StructuredOutputError regression test (opencode_test.go) and a one-line note in the agents guide (docs/src/content/docs/guides/agents.md) so agents know the new error path is handled.

Risk Assessment

✅ Low: The change is a narrowly-scoped bug fix that decodes an existing field on opencode's response, surfaces a clean error, and bumps a retry counter to opencode's default; it is well-tested by a regression test and accompanied by a docs update, with no functional regressions to existing flows.

Testing

I exercised the new regression test in isolation, the full OpencodeAgent test family, and the full race-enabled test suite. To prove the bug existed before the fix and is gone now, I temporarily checked out the pre-fix versions of the four opencode source files into a side-by-side replay, ran a tagged evidence harness that drives a mock opencode HTTP server returning the exact StructuredOutputError payload from run 01KWDTFPNXTC94YEYCN23XFFG1, captured the misleading pre-fix error string (opencode output parse: invalid character 'N' looking for beginning of value (output snippet: ...)) and the clean post-fix error string (opencode structured output failed after 2 internal retries: Model did not produce structured output), then restored the worktree to a clean state with only the target commit's source files. No transient artifacts were left in the worktree; all evidence is under the dedicated evidence directory.

Evidence: New regression test output (TestOpencodeAgent_StructuredOutputError)

=== RUN TestOpencodeAgent_StructuredOutputError --- PASS: TestOpencodeAgent_StructuredOutputError (0.00s) PASS ok github.com/kunchenguid/no-mistakes/internal/agent 0.239s

=== RUN   TestOpencodeAgent_StructuredOutputError
--- PASS: TestOpencodeAgent_StructuredOutputError (0.00s)
PASS
ok  	github.com/kunchenguid/no-mistakes/internal/agent	(cached)
Evidence: Pre-fix behavior: misleading JSON-parse error on reasoning prose

USER-FACING ERROR (PRE-FIX): opencode output parse: invalid character 'N' looking for beginning of value (output snippet: "Now I need to find the failing test. The only failing test is foo.") MISSING: 'structured output failed' - test demonstrates BUG present

=== RUN   TestEvidence_StructuredOutputErrorMessage
    evidence_structured_output_test.go:47: RESULT: <nil>
    evidence_structured_output_test.go:51: USER-FACING ERROR (PRE-FIX): opencode output parse: invalid character 'N' looking for beginning of value (output snippet: "Now I need to find the failing test. The only failing test is foo.")
    evidence_structured_output_test.go:53: MISSING: 'structured output failed' - test demonstrates BUG present
--- PASS: TestEvidence_StructuredOutputErrorMessage (0.00s)
PASS
ok  	github.com/kunchenguid/no-mistakes/internal/agent	0.238s
Evidence: Post-fix behavior: clean StructuredOutputError message with retry count

USER-FACING ERROR (POST-FIX): opencode structured output failed after 2 internal retries: Model did not produce structured output

=== RUN   TestEvidence_StructuredOutputErrorMessage
    evidence_structured_output_test.go:47: RESULT: <nil>
    evidence_structured_output_test.go:51: USER-FACING ERROR (PRE-FIX): opencode structured output failed after 2 internal retries: Model did not produce structured output
--- PASS: TestEvidence_StructuredOutputErrorMessage (0.02s)
PASS
ok  	github.com/kunchenguid/no-mistakes/internal/agent	0.305s
Evidence: Side-by-side user-visible error comparison

USER-FACING ERROR PRODUCED BY THE FIX: opencode structured output failed after 2 internal retries: Model did not produce structured output BEFORE THE FIX THIS SCENARIO WOULD HAVE PRODUCED: opencode output parse: invalid character 'N' looking for beginning of value (output snippet: "Now I need to find the failing test. The only failing test is foo.")

USER-FACING ERROR PRODUCED BY THE FIX:
opencode structured output failed after 2 internal retries: Model did not produce structured output

BEFORE THE FIX THIS SCENARIO WOULD HAVE PRODUCED:
opencode output parse: invalid character 'N' looking for beginning of value (output snippet: "Now I need to find the failing test. The only failing test is foo.")

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

✅ **Review** - passed

✅ No issues found.

✅ **Test** - passed

✅ No issues found.

  • go test -race ./...
  • go test -race ./... (full suite, all packages pass)
  • go test -race -run 'TestOpencodeAgent' -v ./internal/agent/... (all 10 opencode agent tests pass, including the new TestOpencodeAgent_StructuredOutputError)
  • Manually replayed TestEvidence_StructuredOutputErrorMessage against base commit 78c7e606 to confirm the bug is real and the new test would have caught it (pre-fix output captured to evidence)
  • Manually replayed the same test against the target commit 28b63fec to capture the fixed user-facing error string
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

gfiorav added 2 commits July 2, 2026 09:32
…rsing prose

When opencode is invoked with format: {type: json_schema} and the model
fails to call the StructuredOutput tool after the configured internal
retries, opencode sets info.error.name = StructuredOutputError on the
assistant message. The streamed text at that point is reasoning prose,
not a JSON document.

Previously the opencode agent only decoded info.structured and fell
through to finalizeTextResult on the prose, producing the misleading
'invalid character N looking for beginning of value' error. This was the
user-visible symptom on run 01KWDTFPNXTC94YEYCN23XFFG1.

Decode info.error and short-circuit with a precise error that names the
failure and reports the retry count, so the driving agent sees the real
cause. Also bump retryCount from 1 to opencode's schema default of 2 to
match the project's own chosen budget.

Regression test: TestOpencodeAgent_StructuredOutputError feeds a
hand-crafted response with info.error.name = StructuredOutputError and
asserts the error path takes over and Output stays nil.
@kunchenguid kunchenguid merged commit 02009a8 into kunchenguid:main Jul 3, 2026
9 checks passed
@kunchenguid

Copy link
Copy Markdown
Owner

Thanks @gfiorav - merged! Really appreciate the contribution.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants