You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
2.49.0 (also present in 2.48.0 and in the reviewed src/providers/opencode-go-transport.ts of 2.49.0)
Endpoint or capability
POST /v1/messages (Claude Code / Claude Desktop three-party profile), OpenCode Go session affinity on requests that carry no conversation identity. Measured below on /v1/messages; the same helper is shared with the native /v1/chat/completions and the Chat-to-Responses bridge, which pass the same header-derived lane, so a sessionless request on those ingresses branches identically (read from source, not separately measured).
Current behaviour
resolveOpenCodeGoTransport still returns the provider unchanged when no session lane is available:
The lane is derived only from client-supplied identity — sessionLaneIdFromRequest(req.headers) (x-codex-parent-thread-id, thread-id, session_id / session-id) or an inbound x-opencode-session. A minimal Claude request has none of those: Claude Desktop's model-availability probe, and any first request before per-conversation metadata exists, carry no metadata.user_id, no session_id, and no x-opencode-session. The upstream Go request is therefore sent with no x-opencode-session at all.
Console Go handles that inconsistently, and both outcomes are visible in production:
it returns 400 invalid_request_error, which Claude Desktop renders as "Configured model not available";
or it accepts the connection and stalls, exceeding the client's probe budget and getting cancelled, which renders as "Can't reach 127.0.0.1:10100 — the operation was aborted due to timeout".
Request-log evidence from one local instance (surface claude-desktop, provider opencode-go): 4 rows failed with exactly this missing-session error (2026-09-09 12:26:18, 17:41:17, 17:45:21, and 2026-09-10 09:03:29), and 6 rows ended 499 client_closed_request — the one at 2026-09-10 09:01:52 ran durationMs: 7524 before the client gave up. No missing-session row was recorded for any other provider on that surface, so the failure tracks the Go route and not the proxy or the network.
Note this is the remaining branch of #3945, not a regression of it. That fix correctly gives requests with identity a stable lane; requests with no identity at all still bypass the header entirely.
Expected behaviour
Every request routed to the canonical Go destination should carry x-opencode-session, so that no Go request is ever sent without it.
Requests that carry real conversation identity keep the existing stable, per-conversation value.
An explicitly supplied x-opencode-session must keep winning, unchanged.
Minimal redacted request or reproduction
With an authenticated OpenCode Go provider configured in the local proxy (the proxy owns the key; this client sends no credential):
# A Claude-shaped request with no metadata.user_id, no session_id, no x-opencode-session.
curl -sS http://127.0.0.1:10100/v1/messages \
-H 'Content-Type: application/json' \
-H 'anthropic-version: 2023-06-01' \
-d '{"model":"claude-opus-4-8-20261111","max_tokens":16,"messages":[{"role":"user","content":"ping"}]}'
claude-opus-4-8-20261111 is the local Claude Desktop alias assigned to opencode-go/deepseek-v4-flash; any alias mapped onto an opencode-go route reproduces the same path. Adding -H 'x-opencode-session: <uuid>' to the identical request returns 200, which isolates the missing header as the cause.
Actual response or error
Sessionless (fails):
HTTP 400
{"type":"error","error":{"type":"invalid_request_error","message":"Error from provider (Console Go): Request is missing x-opencode-session and cannot be routed efficiently. Please see https://opencode.ai/docs/go/#where-can-i-use-it"}}
Same request with an x-opencode-session header (succeeds):
Intermittent stall variant observed at the client: probe cancelled after 7524 ms and reported as an unreachable gateway (client_closed_request / client_cancel, no upstream response body).
Go does not publish a full header specification; the requirement is stated in the error body and was also communicated directly by updates@opencode.ai (see #3344).
Suggested mapping or implementation notes
The gap is one branch inside the shared helper, so a fix there covers all three ingress paths that call it (/v1/messages → Responses core, native /v1/chat/completions, and the Chat-to-Responses bridge) without touching any caller:
-import { createHash } from "node:crypto";+import { createHash, randomUUID } from "node:crypto";
@@
- if (!sessionLane) return provider;
if (hasHeaderCaseInsensitive(provider.headers, OPENCODE_GO_SESSION_HEADER)) return provider;
@@
- [OPENCODE_GO_SESSION_HEADER]: deriveOpenCodeGoSessionId(sessionLane),+ // Sessionless probes still need affinity; isolate them rather than sharing a conversation.+ [OPENCODE_GO_SESSION_HEADER]: deriveOpenCodeGoSessionId(sessionLane || randomUUID()),
@@
deriveOpenCodeGoSessionId already hashes its input to an opaque ocx_<32 hex> value, so a random lane stays opaque and provider-scoped. The one behaviour worth pinning in a regression test: two consecutive sessionless requests must not receive the same id, while a real lane must stay stable across repeats and an explicit inbound header must pass through untouched.
Additional context and attachments
Locally applied and verified on 2.49.0 with the diff above:
sessionless probe shape, 5 consecutive requests: 200, 200, 200, 200, 200; slowest 4.1 s (probe budget is roughly 7.5 s).
the reported alias plus 5 other Go routes returned 200 after the change; before it, the identical sessionless request returned the 400 above.
after the restart that picked up the change, the request log records 92 Go requests, all 200, and zero missing x-opencode-session rows.
Related: #3945 (fixed affinity when identity is present — this is the remaining sessionless branch), #3344, #3378, #3857.
Checks
I searched existing provider and compatibility issues.
The request and response were redacted.
The expected behaviour is based on an upstream specification or a concrete client requirement.
Client or integration
Claude Code
Provider or upstream service
OpenCode Go (
opencode-go)OpenCodex version
2.49.0 (also present in 2.48.0 and in the reviewed
src/providers/opencode-go-transport.tsof 2.49.0)Endpoint or capability
POST /v1/messages(Claude Code / Claude Desktop three-party profile), OpenCode Go session affinity on requests that carry no conversation identity. Measured below on/v1/messages; the same helper is shared with the native/v1/chat/completionsand the Chat-to-Responses bridge, which pass the same header-derived lane, so a sessionless request on those ingresses branches identically (read from source, not separately measured).Current behaviour
resolveOpenCodeGoTransportstill returns the provider unchanged when no session lane is available:The lane is derived only from client-supplied identity —
sessionLaneIdFromRequest(req.headers)(x-codex-parent-thread-id,thread-id,session_id/session-id) or an inboundx-opencode-session. A minimal Claude request has none of those: Claude Desktop's model-availability probe, and any first request before per-conversation metadata exists, carry nometadata.user_id, nosession_id, and nox-opencode-session. The upstream Go request is therefore sent with nox-opencode-sessionat all.Console Go handles that inconsistently, and both outcomes are visible in production:
400 invalid_request_error, which Claude Desktop renders as "Configured model not available";Request-log evidence from one local instance (surface
claude-desktop, provideropencode-go): 4 rows failed with exactly this missing-session error (2026-09-09 12:26:18,17:41:17,17:45:21, and2026-09-10 09:03:29), and 6 rows ended499 client_closed_request— the one at2026-09-10 09:01:52randurationMs: 7524before the client gave up. No missing-session row was recorded for any other provider on that surface, so the failure tracks the Go route and not the proxy or the network.Note this is the remaining branch of #3945, not a regression of it. That fix correctly gives requests with identity a stable lane; requests with no identity at all still bypass the header entirely.
Expected behaviour
Every request routed to the canonical Go destination should carry
x-opencode-session, so that no Go request is ever sent without it.x-opencode-sessionmust keep winning, unchanged.Minimal redacted request or reproduction
With an authenticated OpenCode Go provider configured in the local proxy (the proxy owns the key; this client sends no credential):
claude-opus-4-8-20261111is the local Claude Desktop alias assigned toopencode-go/deepseek-v4-flash; any alias mapped onto anopencode-goroute reproduces the same path. Adding-H 'x-opencode-session: <uuid>'to the identical request returns 200, which isolates the missing header as the cause.Actual response or error
Sessionless (fails):
Same request with an
x-opencode-sessionheader (succeeds):Intermittent stall variant observed at the client: probe cancelled after
7524 msand reported as an unreachable gateway (client_closed_request/client_cancel, no upstream response body).Upstream documentation
https://opencode.ai/docs/go/#where-can-i-use-it
Go does not publish a full header specification; the requirement is stated in the error body and was also communicated directly by
updates@opencode.ai(see #3344).Suggested mapping or implementation notes
The gap is one branch inside the shared helper, so a fix there covers all three ingress paths that call it (
/v1/messages→ Responses core, native/v1/chat/completions, and the Chat-to-Responses bridge) without touching any caller:deriveOpenCodeGoSessionIdalready hashes its input to an opaqueocx_<32 hex>value, so a random lane stays opaque and provider-scoped. The one behaviour worth pinning in a regression test: two consecutive sessionless requests must not receive the same id, while a real lane must stay stable across repeats and an explicit inbound header must pass through untouched.Additional context and attachments
Locally applied and verified on 2.49.0 with the diff above:
missing x-opencode-sessionrows.Related: #3945 (fixed affinity when identity is present — this is the remaining sessionless branch), #3344, #3378, #3857.
Checks