anomalyco / anomalyco/opencode

bug(session): finish_reason 'unknown' triggers an assistant-prefill continuation; SAP AI Core (and other prefill-forbidding providers) 400 after every successful turn

Open
#46,415 4 comments 0 reactions 1 assignee View on GitHub

@kitlangton is already working on this.

Since Aug 31, 2026.

Dominant language
TypeScript
Stars
209k
Forks
27.5k
PR merge metrics
PR metrics pending

Description

bug(session): finish_reason: unknown triggers an assistant-prefill continuation, so providers that forbid prefill (SAP AI Core) return 400 after every successful turn

Summary

With SAP AI Core (sap-ai-core, @jerome-benoit/sap-ai-provider-v2, e.g. anthropic--claude-4.8-opus), every turn renders the assistant response correctly and is then immediately followed by an extra error:

400 - LLM Module: This model does not support assistant message prefill. The conversation must end with a user message.
Error location: LLM Module

Root cause: the provider's finish reason normalizes to "unknown", which the run loop treats as "turn incomplete, keep going". The loop then re-issues a model request whose message array ends with the assistant message (an assistant prefill / continuation). SAP's gateway rejects prefill, so a spurious 400 appears after each otherwise-successful turn.

Evidence

Querying the local session DB for recent sap-ai-core assistant messages, finish is "unknown" on every completed turn:

finish histogram: { "unknown": 10, "None": 2 }   // "None" == the earlier auth-error turns
model: anthropic--claude-4.8-opus, finish: "unknown", error: false, completed: true   // x10

Server log confirms SAP runs through the AI-SDK path:

llm.native_unsupported_reason="provider is not openai, opencode, or anthropic"

Root cause (code path)

  1. Finish-reason enum is narrow. packages/llm/src/schema/ids.ts:39

    export const FinishReason = Schema.Literals(["stop", "length", "tool-calls", "content-filter", "error", "unknown"])
    

    Note it does not include the AI SDK's "other".

  2. Unrecognized reasons collapse to "unknown". packages/opencode/src/session/llm/ai-sdk.ts:22-24

    function finishReason(value: string | undefined): FinishReason {
      return Schema.is(FinishReason)(value) ? value : "unknown"
    }
    

    @jerome-benoit/sap-ai-provider-v2 reports a finish reason that is not one of the six literals (AI SDK "other" or a raw Claude stop reason), so opencode records finish = "unknown".

  3. The run loop keeps looping on "unknown". packages/opencode/src/session/prompt.ts:1111-1116

    if (
      lastAssistant?.finish &&
      !["tool-calls", "unknown"].includes(lastAssistant.finish) &&
      !hasToolCalls &&
      lastAssistant.parentID === lastUser.id
    ) {
      // exit loop
      break
    }
    

    Because "unknown" is excluded from the exit condition, a completed turn (text produced, no tool calls) does not exit; step++ and the model is called again.

  4. The continuation request is an assistant prefill. With no new user message and no tool results appended, the message array sent on the next iteration ends with the assistant message. Native Anthropic/OpenAI accept this (prefill/continuation), but the SAP AI Core gateway forbids it and returns 400 ... must end with a user message.

Reproduction

  1. Connect sap-ai-core and select anthropic--claude-4.8-opus.
  2. Send any prompt (e.g. "what is 1+1").
  3. The answer streams correctly, then an extra 400 ... does not support assistant message prefill error appears.
  4. Inspect the assistant message: finish === "unknown".

Suggested fixes

  • opencode (robust): do not issue a prefill continuation when finish === "unknown" but the assistant already produced content and there are no pending/unexecuted tool calls — treat it as terminal. Alternatively, gate prefill continuation on a provider capability flag, since many gateways forbid trailing-assistant requests.
  • Finish-reason mapping: map the AI SDK's "other" explicitly (and, for sap-ai-provider-v2, map Claude's end_turn/stop_sequence to "stop") so a clean turn exits the loop instead of collapsing to "unknown".
  • Provider package: @jerome-benoit/sap-ai-provider-v2 should emit a standard "stop" finish reason for completed Claude turns.

Environment

  • opencode dev branch (package version 1.18.23)
  • macOS (Apple Silicon), Bun runtime
  • Provider sap-ai-core via @jerome-benoit/sap-ai-provider-v2, model anthropic--claude-4.8-opus

Related

  • #46354 (SAP AI Core / GitHub Copilot credential handling) — separate subsystem (auth), filed separately.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.