anomalyco / anomalyco/opencode
core(v2): "unknown finish reason" error omits raw provider finish_reason
@kitlangton is already working on this.
Since Aug 22, 2026.
- Dominant language
- TypeScript
- Stars
- 209k
- Forks
- 27.5k
- PR merge metrics
- PR metrics pending
Description
Summary
In OpenCode V2, when a provider returns a non-standard finish_reason, the session step fails with:
Error: session.stream: The provider response ended with an unknown finish reason.
at SessionRunner.runSteps
at SessionRunner.drain
…without including the actual value the provider sent. V2 already captures that raw value internally — assistant messages are persisted with "finish":"error","rawFinish":"sensitive" — so the thrown error could surface it too. Today an opaque retry loop is indistinguishable from truncation/EOF/network issues; with the raw value included, this case is immediately recognizable as a provider-side content filter.
Environment
- opencode version: opencode2 v0.0.0-beta-17898 (V2, beta channel)
- OS: Linux 7.0.0-30-generic x86_64 (Ubuntu)
- Terminal: ghostty (TERM=xterm-ghostty, COLORTERM=truecolor)
- Shell: /usr/bin/zsh
- Install/channel: beta
- Active plugins: orchestator.ts, pushover-notifier.ts, rtk.ts (local), opencode-key-rotator (local), @slkiser/opencode-quota@latest, opencode-vibeguard@latest
Reproduction
- Use the built-in
opencodeprovider with a free model whose operator applies server-side safety filtering. In my caseopencode/x-preview-f-free— listed as "Ox Alpha Free (Unlimited)", a stealth reasoning preview model (release_date 2026-08-21) in the models.dev catalog used by OpenCode. - Send prompts whose output trips the operator's sensitive-content filter.
- The stream gets cut mid-generation after partial reasoning/text output.
Intermittent by nature of provider filtering; in one V2 session it happened 24 times (49 automatic retries) across ~10 hours, ending in Failed to drain Session.
Expected Behavior
The error should include the unrecognized raw value so users can diagnose the cause, e.g.:
The provider response ended with an unknown finish reason: "sensitive"
Suggested Fix
- In
packages/opencode/src/session/llm/ai-sdk.ts, the adapter already readsevent.rawFinishReasonand special-cases"network_error"into a descriptive failure:ProviderError.ResponseStreamError("Provider finish_reason: network_error"). Applying the same pattern to unrecognized reasons — or simply appending the raw value to the "unknown finish reason" error — would close the observability gap. - Include the raw finish-reason string whenever it fails validation instead of discarding it.
Actual Behavior
Generic message with no indication of what the provider returned:
level=ERROR message="Failed to drain Session" cause="AI.Error: session.stream: The provider response ended with an unknown finish reason."
Notably, despite the throw, the partial content IS persisted along with the raw value — querying session storage (session_message) for that session shows:
SELECT json_extract(data,'$.finish'), json_extract(data,'$.rawFinish'), COUNT(*)
FROM session_message WHERE session_id='...' AND type='assistant'
GROUP BY 1,2;
-- error|sensitive|24
-- tool-calls|tool_calls|15
-- stop|stop|8
-- error||(empty)|4
So somewhere between stream ingestion and the thrown error the raw value is dropped, even though it demonstrably reaches persistence.
Additional Context
- Related issues: #18813 (non-standard finish reason treated as unknown causes loop), #43622 / #41469 / #37852 (fallout of
finish=unknown). None cover surfacing the raw value in the error itself.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.