Repository navigation
[Provider]: Union Alpha inference fails on OpenCode routes; per-model Anthropic protocol mismatch #4847
Description
Client or integration
Codex App; also reproduced with minimal direct HTTP requests.
Provider or upstream service
OpenCode / Union Alpha (union-alpha). Reproduced on opencode-go; the same failure is observed on opencode-zen. Please also consider the shared model protocol metadata for opencode-free (related catalog issue #4826).
OpenCodex version
2.57.0 (installed CLI). macOS, Apple Silicon. Observed September 16, 2026.
Endpoint or capability
Outbound protocol selection: /zen/go/v1/chat/completions versus /zen/go/v1/messages; corresponding Zen /zen/v1 routes. Inbound Codex Responses requests and minimal Chat Completions requests.
Current behaviour
Union Alpha works in the OpenCode application but fails through OpenCodex with upstream_server_error / HTTP 500. Local OpenCode history confirms successful responses for provider opencode-go, model union-alpha; it is not merely a different model selected under the “Free” display name.
Both clients use the same configured Go base URL and credential (equality checked locally; no credentials included). OpenCodex config uses provider-wide adapter: openai-chat. The local OpenCode model catalog instead specifies a per-model override for Union Alpha: provider.npm: @ai-sdk/anthropic, although the provider default is OpenAI-compatible.
The displayed proxy error alone does not prove an internal OpenCodex crash: a minimal direct Chat Completions request also returns 500. This points toward a protocol/request compatibility issue; it does not establish the full root cause.
Expected behaviour
Select the upstream protocol supported by Union Alpha without changing the adapter for other models on the same provider. Preserve the distinction between model availability, upstream service failures and proxy failures. Clarify any documented client/session requirements rather than assuming every model at a mixed-protocol gateway accepts Chat Completions.
The application/catalog evidence supports investigating the Anthropic Messages path; successful generation through that path has not yet been validated in the direct reproduction below.
Minimal redacted request or reproduction
# Local proxy, configured with opencode-go and its normal API key.
# No secrets or private prompt content are included in this request.
curl http://127.0.0.1:10100/v1/chat/completions \
-H 'Content-Type: application/json' \
-d '{"model":"opencode-go/union-alpha","messages":[{"role":"user","content":"Reply only OK."}],"max_tokens":32,"stream":false}'Observed comparison using the same credential and Go base URL:
- Direct
POST /zen/go/v1/chat/completions, minimal message, no reasoning field: 500. - Same direct call with
reasoning_effort: max: 500. - Equivalent minimal request through OpenCodex: 500.
- Direct
POST /zen/go/v1/messageswithx-api-key,anthropic-version: 2023-06-01, and no session header: 400MissingSessionID. - Add a fresh diagnostic
x-opencode-sessionvalue: 503Endpoint is unavailable. - Chat Completions with that same diagnostic session header still returns 500.
A first Python HTTP-client attempt received a separate 403/1010 response; direct comparisons above were repeated using Node fetch to avoid conflating that client-specific rejection with the 500s.
Actual response or error
Proxy:
{"error":{"message":"Provider error 500: Internal server error","type":"server_error","code":"upstream_server_error"}}Direct Chat Completions:
{"error":{"type":"error","message":"Internal server error"}}Direct Messages without session header:
{"type":"error","error":{"type":"MissingSessionID","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"}}Direct Messages with diagnostic session header:
{"type":"error","error":{"type":"api_error","message":"Error from provider (Console Go): Upstream request failed: Endpoint is unavailable."}}Both low and max reasoning requests have failed through the proxy; this is not isolated to max. Two benchmark attempts failed before editing any files. The harness produced repeated 500s; no benchmark score was attributed to the model.
Upstream documentation
https://opencode.ai/docs/zen/
https://opencode.ai/docs/go/#where-can-i-use-it
Suggested mapping or implementation notes
Investigate a per-model protocol override for union-alpha across the OpenCode provider entries, based on upstream model metadata. Do not switch the entire mixed-model provider to Anthropic. Check documented authentication/session requirements and reasoning translation, and add regression coverage that leaves existing Chat Completions models unchanged.
This is a diagnostic hypothesis, not a tested patch. The 503 on the Messages path still requires investigation. No request to bypass upstream access restrictions is intended.
Additional context and attachments
Related: #4826, which primarily reports exclusion from the opencode-free catalog and also mentions the protocol mismatch. That issue was closed automatically for template formatting; this report follows the provider compatibility template and focuses on inference failures. It does not claim #4826 has been fixed.
No global configuration was changed during the comparison. Exact endpoint reproduction was performed for Go; Zen is a separate observed failure, and Free was not independently inference-tested in this investigation. No proposed PR yet.
Checks
- I searched existing provider compatibility issues.
- The request and response examples are redacted.
- The expected behaviour is based on upstream specification or a concrete client requirement.
Activity
lidge-jun commentedon Sep 16, 2026
리뷰 · 우선순위 72 / 80
이 이슈는 OpenCode Go/Zen 쪽 Union Alpha(
union-alpha)가 OpenCodex를 거치면 Chat Completions로 500(upstream_server_error)이 나고, 같은 키·같은 Go base URL로 직접 쳐도 Chat는 500, Anthropic Messages는 세션 헤더 없으면MissingSessionID, 넣으면 503이라는 보고다. 보고자는 OpenCode 앱 로컬 카탈로그가 Union Alpha만provider.npm: @ai-sdk/anthropic으로 두고 나머지는 OpenAI 호환 기본값을 쓴다고 적었다. 즉 "프로바이더 전체를 Anthropic으로 바꾸라"가 아니라, 모델 하나만에 프로토콜 예외가 필요하다는 진단이다. 관련#4826(opencode-free 카탈로그 제외)은 템플릿 형식으로 이미 닫혔고, 이 이슈는 추론 실패에 초점을 맞춘 provider-compatibility 보고라서 중복으로 닫을 대상은 아니다.지금
devHEAD8c5917448e971f8bbfb7595252932f678b486a35(package 2.58.0)를 보면 방향이 맞는지 바로 확인할 수 있다.src/providers/registry/entries-core.ts의opencode-go행은 프로바이더 기본 어댑터가openai-chat이고, baseUrl은https://opencode.ai/zen/go/v1이다. 이미 혼성 와이어를modelWireDefaults로 다루는 선례가 있다.gpt-5.6-luna/grok-4.6/muse-spark-1.3-contributor등은openai-responses로 핀한다. 또src/types/wire.ts의ANTHROPIC_WIRE_MODELS["opencode-go"]에는minimax-m2.5/minimax-m2.7/minimax-m3만 들어 있다.union-alpha는 생성 메타데이터(src/generated/model-metadata.tsopencode-go 목록)에도, 위 예외 목록에도 없다. 그래서 기본 경로는 Chat Completions로 나가며, 보고된 직접 curl 500과 같은 프로토콜 불일치가 재현될 여지가 크다.다만 "Anthropic으로만 바꾸면 끝"은 아직 증명되지 않았다. 본문 재현 4·5번이 Messages 경로에서 세션/엔드포인트 거절을 보여 주고, OpenCodex가
x-opencode-session을 임의로 위장 발급하지 않는 정책은opencode-freenote(entries-extended.ts)에도 이미 적혀 있다. 또한src/types/wire.ts의MODEL_ADAPTER_OVERRIDE_ALLOWED는 지금openai-chat과openai-responses만 허용한다. 사용자가modelAdapters.union-alpha = "anthropic"을 써도src/server/adapter-resolve.ts의resolveWireProtocolOverride가 그 값을 채택하지 않는다. Anthropic 핀은 레지스트리ANTHROPIC_WIRE_MODELS(또는 동등한 하드 핀) 확장으로만 열린다. types.ts/config.ts 분리 캠페인과 무관하므로 "닫고 리베이스" 대상이 아니다.경로/심볼 -
src/providers/registry/entries-core.tsopencode-go— 기본adapter: "openai-chat".union-alpha용modelWireDefaults/ANTHROPIC_WIRE_MODELS항목 없음.경로/심볼 -
src/types/wire.tsMODEL_ADAPTER_OVERRIDE_ALLOWED— anthropic 미포함. 설정만으로 Anthropic 전환 불가.경로/심볼 -
src/types/wire.tsANTHROPIC_WIRE_MODELS["opencode-go"]— minimax 세 개만. Union Alpha 가설을 코드에 심을 자리.경로/심볼 -
src/server/adapter-resolve.tsresolveWireProtocolOverride— pin → modelAdapters → modelWireDefaults → provider adapter 순서. anthropic은 pin/레지스트리 경로여야 함.경로/심볼 -
src/providers/opencode-go-transport.ts/ Zen rate-limit — 세션·업로드 거절과 겹치면 Messages 전환만으로 503이 남을 수 있음. 본문 재현 5번과 맞춰 봐야 한다.관련 -
#4826은 카탈로그 제외·템플릿 닫힘. 이 이슈는 추론 실패라서 별개로 유지하는 편이 맞다.메인테이너의 판단이 필요한 지점
ANTHROPIC_WIRE_MODELS에 넣을지, 아니면 먼저 Messages 경로가 세션 없이도(또는 문서화된 제3자 세션 계약으로) 200을 내는지 증거부터 모을지opencode-zen/opencode-go/opencode-free에 같은 모델 id가 있을 때 예외를 프로바이더별로 복제할지, destination 공통 메타로 올릴지너의 추천
이슈는 열어 두고, 다음 PR은 "가설 확정"부터 하라. (1) 직접 Messages로 Union Alpha가 정상 생성되는지(세션 요구 포함) redacted 증거를 이슈에 한 번 더 붙이고, (2) 증거가 서면
ANTHROPIC_WIRE_MODELS["opencode-go"]에union-alpha를 추가하는 작은 레지스트리 PR을 열어라. 프로바이더 전체를 Anthropic으로 바꾸거나 세션 헤더를 위장 발급하는 방향은 추천하지 않는다. Chat만 고치려다 Responses/minimax 예외 선례를 깨지 않도록 회귀 테스트에 "다른 Go 모델은 여전히 chat/responses 기본"을 넣어라.이 댓글은 grok-bot이 작성했습니다
lidge-jun commentedon Sep 17, 2026
Closed by #4930 on
dev, squashed asd20b25d47068e6b2a40fe561d6ca8ed449046df7.Your diagnosis held: this needed a per-model protocol exception, not a provider-wide adapter change.
union-alpha's exact model id is now pinned to the Anthropic wire foropencode-go, and sibling Chat and Responses models on the same provider are untouched.Two things surfaced while fixing it that your reproduction had already half-exposed. Generated
x-opencode-sessionvalues were not namespaced by wire protocol, so a lane could carry a session minted under a different protocol — that is now namespaced with a versioned hash separator, and an operator-supplied header still wins unchanged. Separately, destination recognition was sensitive to wire-selection order: once the wire flipped to Anthropic, the registry matcher no longer recognized the already-flipped provider. Each request now retains the original routed provider for the registry check and settles the final wire separately.On the part that is not ours: step 5 of your reproduction still reproduces. A direct Messages call with a fresh session header returns 503
Endpoint is unavailable, and that is upstream service availability, not protocol selection. This change does not establish successful Union Alpha inference, and saying otherwise would be a claim the evidence does not support. What it does deliver is the expected behaviour you asked for — the upstream protocol Union Alpha declares is now the one selected, and model availability, upstream failures and proxy failures stay distinguishable rather than collapsing into a single 500.If inference still fails once the Go endpoint is serving again, that is a new report rather than this one, and the trace will now name which of the three it is. Shipping in the next release off
dev.