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)
@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/providerson the serving instance lists the provider withsource: "api"and the correctkeyvalue, so the config layer sees the key — it's the outgoing request that lacks theAuthorizationheader.- Reproduces for multiple
source: "api"providers (zhipuai-coding-plan, deepseek, alibaba-token-plan-cn). - Adding
variant: "default",delivery: "queue", orresume: trueto 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
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.