Skip to content

opencode-free: Zen rejects requests without x-opencode-session (free tier lock-in) #4121

Description

@Hydroxide8006

Client or integration

Direct HTTP client

Provider or upstream service

opencode-free (https://opencode.ai/zen/v1)

OpenCodex version

2.10.2

Endpoint or capability

/v1/chat/completions (free models, e.g. mimo-v2.5-free)

Current behaviour

Requests via the opencode-free provider fail on free models. Upstream returns:

{"type":"error","error":{"type":"MissingSessionID","message":"Error from provider (Console): OpenCode's free tier can only be used in OpenCode"}}

The proxy currently only sends x-opencode-client: desktop (see opencode-free entry in src/providers/registry.ts), which is not sufficient for the Zen free-tier gate.

Expected behaviour

Free-model requests through opencode-free should succeed (HTTP 200, usage with cost 0), matching what the real OpenCode client gets when it sends its session headers.

Minimal redacted request or reproduction

# 1. Direct upstream repro (no keys involved, keyless free tier):
# Fails - same error the proxy currently triggers:
curl -s -X POST https://opencode.ai/zen/v1/chat/completions   -H 'Content-Type: application/json'   -H 'x-opencode-client: desktop'   -d '{"model":"mimo-v2.5-free","messages":[{"role":"user","content":"say ok"}],"max_tokens":1}'

# Passes - adding any x-opencode-session value clears the gate:
curl -s -X POST https://opencode.ai/zen/v1/chat/completions   -H 'Content-Type: application/json'   -H 'x-opencode-client: desktop'   -H 'x-opencode-session: ses_test123000000000000000001'   -H 'User-Agent: opencode/1.18.30'   -d '{"model":"mimo-v2.5-free","messages":[{"role":"user","content":"say ok"}],"max_tokens":1}'

# 2. Through the proxy:
# ocx start (default port 10100), provider opencode-free configured, then:
curl -s -X POST http://127.0.0.1:10100/v1/chat/completions   -H 'Content-Type: application/json'   -d '{"model":"opencode-free/mimo-v2.5-free","messages":[{"role":"user","content":"say ok"}],"max_tokens":5}'
# Before fix: MissingSessionID error. After adding x-opencode-session to
# providers.opencode-free.headers in config + restart: HTTP 200.

Actual response or error

{"type":"error","error":{"type":"MissingSessionID","message":"Error from provider (Console): OpenCode's free tier can only be used in OpenCode"}}

Probe matrix (all redacted, no credentials):

  • bare request -> MissingSessionID
  • x-opencode-client only -> MissingSessionID
  • User-Agent opencode/1.18.30 only -> MissingSessionID
  • x-opencode-session set to any value (even unformatted) -> gate passed (200 or quota error, never MissingSessionID)

So the gate checks presence of x-opencode-session, not User-Agent and not session format.

Upstream documentation

No public specification exists for these headers; they were reverse-engineered from the opencode-ai binary (v1.18.30). For providerID starting with "opencode" it sends: x-opencode-project (optional), x-opencode-session (ses_...), x-opencode-request (msg_...), x-opencode-client, User-Agent opencode/. Zen docs: https://opencode.ai/docs/zen/

Suggested mapping or implementation notes

In the openai-chat adapter (or a Zen-specific wrapper) when baseUrl is https://opencode.ai/zen/v1, generate per request:

  • x-opencode-session: ses_ (fresh per request)
  • x-opencode-request: msg_ (fresh per request)
  • User-Agent: opencode/ (only if caller did not set one)
  • keep existing x-opencode-client: desktop

Per-request generation mirrors the real client. A static session ID in config works today but risks future per-session rate-limiting.

Additional context and attachments

Related: free-tier gate appears new (previously x-opencode-client alone was enough). Note on the earlier lock-in error string: it is returned with error type MissingSessionID, which is what pointed at the session header.

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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    landed-via-maintainerOriginal PR closed after landing via a maintainer merge trainproviderProvider adapters, OpenAI-compat presets, upstream API quirksprovider-compatibilityProvider compatibility reports

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions