Skip to content

[Bug]: No management-plane support for provider headers — custom providers needing client fingerprints (Agent Router) 401 after headers are lost #959

Description

@brunoflma

Client or integration

Direct HTTP/API client (Codex App / Claude Code also affected via the proxy)

Area

Proxy and routing · Provider adapter

Summary

The provider management API and CLI have no way to set or restore the headers field on a provider. PATCH /api/providers accepts only a fixed allowlist of fields (disabled, adapter, baseUrl, defaultModel, authMode, apiKeyTransport, note, allowPrivateNetwork, liveModels, codexAccountMode, setDefault) and ocx provider edit exposes the same set as flags — there is no headers option. If a custom provider's headers block is lost (e.g. the provider entry is re-saved via POST /api/providers without round-tripping it, or a config migration drops it), the only recovery is hand-editing ~/.opencodex/config.json and restarting the proxy.

Concrete failure: the Agent Router gateway (agentrouter.org) rejects requests that carry a valid API key but not the Claude Code CLI client fingerprint (User-Agent, x-app, X-Stainless-*, anthropic-version, anthropic-beta). The openai-chat adapter sends Authorization: Bearer <key> and then merges provider.headers; without the fingerprint headers the gateway returns 401 unauthorized client detected. In this install the headers block on the AGR-OAI provider (present in a 2026-07-29 config backup) was missing from the 2026-08-02 config, and every routed request failed with that 401 until the block was restored by hand.

Expected: PATCH /api/providers and ocx provider edit should accept headers (merge or replace), and any provider save path should round-trip unknown provider fields so custom headers survive.

Reproduction

  1. Install @bitkyc08/opencodex 2.10.0 and start the proxy (ocx start).
  2. Add a custom provider that requires fingerprint headers, e.g. Agent Router, in ~/.opencodex/config.json:
    {
      "providers": {
        "AGR-OAI": {
          "adapter": "openai-chat",
          "baseUrl": "https://agentrouter.org/v1",
          "authMode": "key",
          "apiKey": "sk-<redacted>",
          "headers": {
            "User-Agent": "claude-cli/2.1.219 (external, sdk-cli)",
            "anthropic-version": "2023-06-01",
            "anthropic-beta": "claude-code-20250219,interleaved-thinking-2025-05-14,effort-2025-11-24",
            "x-app": "cli",
            "X-Stainless-Retry-Count": "0",
            "X-Stainless-Package-Version": "0.94.0"
          }
        }
      }
    }
  3. Try to manage the headers through the management plane:
    ocx provider edit AGR-OAI --enabled on          # works, headers preserved
    ocx provider edit AGR-OAI --headers '{"x-app":"cli"}'   # no such flag; exits with usage error
    
    Or via API:
    PATCH /api/providers?name=AGR-OAI
    { "headers": { "x-app": "cli" } }
    → 400 {"error":"no recognized fields to update"}
    
  4. Simulate headers being lost: POST /api/providers with a provider payload that omits headers (the field is optional and not validated as required), then send a request with an image-free message:
    POST /v1/responses
    { "model": "AGR-OAI/gpt-5.6-sol", "input": [...] }
    → 401 {"error":{"message":"unauthorized client detected, contact support for assistance at https://discord.gg/aYq5B4RW3"},"message":"UNAUTHENTICATED","success":false,"type":"unauthorized_client_error"}
    
  5. Observe: only a hand edit of ~/.opencodex/config.json (restoring headers) plus a restart fixes it; the management plane cannot.

Version

2.10.0 (npm @bitkyc08/opencodex); Codex runtime 0.146.0

Operating system

Windows 11 (current), America/Fortaleza timezone

Provider and model

agentrouter.org via custom provider AGR-OAI (openai-chat adapter), model gpt-5.6-sol; same issue applies to any provider needing custom headers (e.g. AGR anthropic adapter uses the same header block)

Logs or error output

Provider error 401: {"error":{"message":"unauthorized client detected, contact support for assistance at https://discord.gg/aYq5B4RW3"},"message":"UNAUTHENTICATED","success":false,"type":"unauthorized_client_error"}

Verified live: GET https://agentrouter.org/v1/models with Authorization: Bearer <key> and no fingerprint headers → 401; with the claude-cli headers added → 200 and the model list is returned.

Redacted configuration

{
  "providers": {
    "AGR-OAI": {
      "adapter": "openai-chat",
      "baseUrl": "https://agentrouter.org/v1",
      "authMode": "key",
      "apiKey": "sk-<redacted>",
      "headers": {
        "User-Agent": "claude-cli/2.1.219 (external, sdk-cli)",
        "anthropic-version": "2023-06-01",
        "anthropic-beta": "claude-code-20250219,interleaved-thinking-2025-05-14,effort-2025-11-24",
        "anthropic-dangerous-direct-browser-access": "true",
        "x-app": "cli",
        "X-Stainless-Retry-Count": "0",
        "X-Stainless-Timeout": "600",
        "X-Stainless-Lang": "js",
        "X-Stainless-Package-Version": "0.94.0",
        "X-Stainless-OS": "MacOS",
        "X-Stainless-Arch": "arm64",
        "X-Stainless-Runtime": "node",
        "X-Stainless-Runtime-Version": "v26.3.0"
      }
    }
  }
}

Code references

  • PATCH /api/providers field allowlist: src/server/management/provider-routes.ts — only disabled, adapter, baseUrl, defaultModel, authMode, apiKeyTransport, note, allowPrivateNetwork, liveModels (plus the exclusive codexAccountMode / setDefault paths); unknown fields → 400 "no recognized fields to update".
  • CLI flags mirror that allowlist: src/cli/provider-runtime.ts (ocx provider edit ... has no --headers).
  • The adapter applies provider.headers after the default Authorization header: src/adapters/openai-chat.ts and src/adapters/anthropic.ts.
  • providerHeadersConfigError in src/config.ts already validates the headers shape, so the schema supports it — only the management-plane mutation paths are missing it.

Suggested fix

  1. Accept headers (object) in PATCH /api/providers, merging or replacing per field semantics, validated by providerHeadersConfigError.
  2. Add a --headers <json> option to ocx provider edit (or a dedicated management route).
  3. Ensure POST /api/providers save paths (GUI/CLI) round-trip unknown provider fields like headers so a provider re-save does not silently drop them.

Checks

  • I searched existing issues and documentation.
  • I removed secrets, tokens, account details, request credentials, and personal data.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingproxyHTTP proxy, routing, reverse-proxy / management auth

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions