anomalyco / anomalyco/opencode

opencode serve: provider auth (auth.json key) not attached to LLM requests for sessions created via the v2 API (/api/session) — works on v1 (/session)

Open
#47,888 1 comment 0 reactions 1 assignee View on GitHub

@kitlangton is already working on this.

Since Sep 8, 2026.

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

Description

Description

When running opencode serve, any session created and prompted through the v2 API (POST /api/session + POST /api/session/{id}/prompt) fails with a provider-side 401, because the provider key from ~/.local/share/opencode/auth.json is not attached to the outgoing LLM request. The v1 API (POST /session + POST /session/{id}/message) on the same serving instance, and the CLI (opencode run -m provider/model) in the same directory, work fine.

The v2 session/prompt calls themselves succeed (HTTP 200, prompt admitted), so callers can't see the failure through the API response. The failure is recorded on the assistant message row as finish: "error", and — importantly — no error event is emitted on the session event stream, so SDK clients just watch the turn stall forever.

Evidence

Same machine, same directory, same provider (zhipuai-coding-plan, key stored in auth.json, source: "api" in /config/providers):

Path Result
CLI opencode run -m zhipuai-coding-plan/glm-5.2 "Reply with exactly: ok" ✅ replies ok
v1 SDK client.session.create() + client.session.prompt({ body: { parts: [...], model } })opencode serve ✅ replies ok in ~13s
v2 SDK client.v2.session.create({ location, agent, model }) + client.v2.session.prompt({ prompt: { text } }) → same opencode serve ❌ 401

The failing turn's assistant message (fetched via GET /api/session/{id}/message):

{
  "type": "assistant",
  "model": {"id": "glm-5.2", "providerID": "zhipuai-coding-plan", "variant": "default"},
  "finish": "error",
  "error": {
    "type": "unknown",
    "message": "Provider request failed with HTTP 401: {\"error\":{\"code\":\"1001\",\"message\":\"Header中未收到Authorization参数,无法进行身份验证。\"}}"
  }
}

(The provider message translates to: "Authorization parameter not received in header, unable to authenticate.")

Notes:

  • /config/providers on the serving instance lists the provider with source: "api" and the correct key value, so the config layer sees the key — it's the outgoing request that lacks the Authorization header.
  • Reproduces for multiple source: "api" providers (zhipuai-coding-plan, deepseek, alibaba-token-plan-cn).
  • Adding variant: "default", delivery: "queue", or resume: true to the v2 prompt makes no difference — always the same 401.
  • Sessions created via v2 without an explicit model also fail the same way, so it's not model-ref resolution.
Plugins

None needed to reproduce. (Machine has plugins installed globally, but the repro server is a bare opencode serve with OPENCODE_CONFIG_CONTENT only defining custom agents.)

OpenCode version
opencode 1.18.29

(@opencode-ai/sdk 1.17.13 for the SDK repro)

Steps to reproduce
mkdir -p /tmp/oc-clean-dir && cd /tmp/oc-clean-dir
opencode serve --hostname=127.0.0.1 --port=4098 &

# v2 → FAILS (assistant message gets finish:"error", provider 401)
node -e '
import("@opencode-ai/sdk/v2").then(async (m) => {
  const client = m.createOpencodeClient({ baseUrl: "http://127.0.0.1:4098" });
  const created = await client.v2.session.create(
    { location: { directory: "/tmp/oc-clean-dir" }, agent: "build",
      model: { providerID: "zhipuai-coding-plan", id: "glm-5.2", variant: "default" } },
    { throwOnError: true });
  await client.v2.session.prompt(
    { sessionID: created.data.data.id, prompt: { text: "Reply with exactly: ok" } },
    { throwOnError: true });
  console.log("prompt admitted — now fetch GET /api/session/<id>/message and check finish/error");
})'

# v1 → WORKS on the same server
node -e '
import("@opencode-ai/sdk/client").then(async (m) => {
  const client = m.createOpencodeClient({ baseUrl: "http://127.0.0.1:4098" });
  const created = await client.session.create({ query: { directory: "/tmp/oc-clean-dir" } }, { throwOnError: true });
  const res = await client.session.prompt(
    { path: { id: created.data.id },
      body: { parts: [{ type: "text", text: "Reply with exactly: ok" }],
              model: { providerID: "zhipuai-coding-plan", modelID: "glm-5.2" } } },
    { throwOnError: true });
  console.log("v1 replied");
})'

Also worth noting for the error path: even when the turn errors out this way, the session event stream (/api/session/{id}/event SSE) stays silent — only session.next.prompt.admitted and session.next.prompted events arrive. Emitting an error event (or setting an abort/error terminal state) would let SDK clients fail fast instead of polling until timeout.

Screenshot and/or share link

n/a (headless serve + SDK repro)

Operating System

Ubuntu 22.04 x86_64, Node v24.14.0

Terminal

headless (opencode serve), not TUI

Context: this breaks DevSpace's opencode subagent integration (Waishnav/devspace#302), which uses the v2 SDK exclusively.

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.