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.

Activity

  1. github-actions commented on Aug 3, 2026

    @github-actions
    Contributor

    Automated translation bookkeeping — detected language: unknown.

  2. added
    bugSomething isn't working
    proxyHTTP proxy, routing, reverse-proxy / management auth
    on Aug 3, 2026
  3. Ingwannu commented on Aug 4, 2026

    @Ingwannu
    Owner

    Maintainer triage: the core management-plane gap is confirmed. The config schema and adapters support validated custom headers, but current PATCH /api/providers and ocx provider edit expose no way to set, merge, or clear them.

    Draft PR #961 is the active implementation, so I will not duplicate the contributor work. Its current merge/clear semantics and sensitive-header validation are directionally correct, but the draft is not review-ready yet: a casing-only update can preserve conflicting keys such as X-Foo and x-foo, and concurrent PATCH requests can still persist stale provider snapshots and erase each other. The documentation also needs to state the validation and clear behavior consistently.

    One scope clarification: ocx provider edit ... --enabled already uses PATCH and preserves existing headers. POST /api/providers is an overwrite operation, so omission replaces the submitted provider shape; the independently confirmed bug is that the supported management surfaces cannot safely edit or restore the header block.

    Leaving this open and linked to #961 until those blockers are addressed and the focused management/CLI tests are green.

  4. added a commit that references this issue on Aug 5, 2026
  5. lidge-jun commented on Aug 5, 2026

    @lidge-jun
    Owner

    Fixed on dev in #1033 (merged as 51c4be686).

    Custom provider headers can now be managed through PATCH /api/providers/:name and ocx provider edit --headers <json>, with merge semantics and {} / - to clear. Header names are matched case-insensitively and static headers are restored correctly.

    Two things are deliberately constrained. The validator rejects standard credential header names (Authorization, X-Api-Key, Cookie, …) and points at apiKey / authMode instead, and GET /api/providers exposes only hasHeaders — presence, never names or values — matching the existing presence-only DTO on /api/config.

    Worth reading before you use it: --headers is documented in all five locales as non-secret request metadata. The validator cannot recognize an arbitrary name like X-My-Token, and the JSON is a command-line argument, so a secret placed there lands in shell history and the process list, and persists in config.json in cleartext unlike API keys.

    Thanks to @Yuxin-Qiao — #961 is the implementation that shipped, carried with authorship intact.

    Not in a release yet. This is on dev, and main is at v2.10.0. It ships in the next release.

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

    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