# NousResearch/hermes-agent · Codex backend extra_headers handling fixes

## Open now

- Nothing open on GitHub.

These issues and PRs address problems with extra_headers being incorrectly sent to the Codex/chatgpt.com backend, causing HTTP 400 errors, alongside related work on session and cache-routing headers.

## TL;DR

- 5 member(s): 0 open issue(s), 1 closed issue(s), 0 open PR(s), 2 merged PR(s), 2 PR(s) closed without merging.
- A fix was merged to main in PR #45633. Verify it addresses your case.
- **Recommended:** read PR #45633 (and any competing PRs below) before filing. Confirm the merged change covers your symptom.

## Maintainer resolution verdict

- Internal-tracker verdict over this cluster's issue members: `resolved` · deciding PR(s): `pr-45633`. This is distinct from the fix status above. Weigh it yourself.

## How this cluster was grouped

- Link evidence (`STRUCTURAL_SPANNING`): every member is connected by structural evidence: shared changed files, or a closing/superseded reference.
- Pairwise link density `0.40`: the share of member pairs that carry a link, counting a structural link in full and a relevance-only link at 0.36. `1.00` means every pair is linked.

## Closed / duplicate issues

- #26599: \[Bug\]: Codex backend rejects 'extra_headers' field in Responses payload (HTTP 400)

## Prior & competing PRs

- **#45633: fix(codex): drop extra_headers for chatgpt.com backend**. merged to main · fix tier `verified`
  - maintainer direction: category `merged`; implication `superseded`
  - files: `agent/transports/codex.py`, `tests/agent/transports/test_codex_transport.py`
- **#47836: fix(codex): restore session_id/x-client-request-id HTTP headers for cache routing (#47335)**. merged to main · fix tier `verified`
  - maintainer direction: category `merged`; implication `superseded`
  - files: `agent/transports/codex.py`, `tests/agent/transports/test_codex_transport.py`
  - fix hunks:
  - `agent/transports/codex.py`

    ```diff
    @@ -218,10 +218,28 @@ def build_kwargs(
                 kwargs.pop("timeout", None)
     
             if is_codex_backend:
    -            # chatgpt.com/backend-api/codex rejects body-level
    -            # ``extra_headers`` with HTTP 400. Correlation/cache routing for
    -            # this backend must not be sent through the Responses payload.
    -            kwargs.pop("extra_headers", None)
    +            # The Codex backend rejects body-level ``extra_headers`` with
    +            # HTTP 400, but the OpenAI SDK's ``extra_headers`` kwarg maps
    +            # to actual HTTP request headers (not body fields).  We need
    +            # these headers for cache-scope routing so prompt cache hits
    +            # remain high.  Send session_id / x-client-request-id as HTTP
    +            # headers while keeping ``prompt_cache_key`` in the body for
    +            # standard OpenAI routing as a belt-and-braces fallback.
    +            cache_scope_id = str(session_id or "").strip()
    +            if cache_scope_id:
    +                existing_extra_headers = kwargs.get("extra_headers")
    +                merged_extra_headers: Dict[str, str] = {}
    +                if isinstance(existing_extra_headers, dict):
    +                    merged_extra_headers.update(
    +                        {
    +                            str(key): str(value)
    +                            for key, value in existing_extra_headers.items()
    +                            if key and value is not None
    +                        }
    +                    )
    +                merged_extra_headers["session_id"] = cache_scope_id
    +                merged_extra_headers["x-client-request-id"] = cache_scope_id
    +                kwargs["extra_headers"] = merged_extra_headers
     
             max_tokens = [redacted:secret-assignment]"max_tokens")
             if max_tokens is not None and not is_codex_backend:
    ```
  - `tests/agent/transports/test_codex_transport.py`

    ```diff
    @@ -155,7 +155,9 @@ def test_codex_backend_no_max_output_tokens(self, transport):
             )
             assert "max_output_tokens" not in kw
     
    -    def test_codex_backend_does_not_set_extra_headers(self, transport):
    +    def test_codex_backend_sets_cache_routing_headers(self, transport):
    +        """Codex backend sends session_id / x-client-request-id as HTTP
    +        headers (via extra_headers) for cache-scope routing."""
             messages = [{"role": "user", "content": "Hi"}]
     
             kw = transport.build_kwargs(
    ```

    ```diff
    @@ -166,9 +168,23 @@ def test_codex_backend_does_not_set_extra_headers(self, transport):
                 is_codex_backend=True,
             )
     
    +        headers = kw.get("extra_headers", {})
    +        assert headers.get("session_id") == "conv-codex-1"
    +        assert headers.get("x-client-request-id") == "conv-codex-1"
    +
    +    def test_codex_backend_no_headers_without_session_id(self, transport):
    +        messages = [{"role": "user", "content": "Hi"}]
    +
    +        kw = transport.build_kwargs(
    +            model="gpt-5.4",
    +            messages=messages,
    +            tools=[],
    +            is_codex_backend=True,
    +        )
    +
             assert "extra_headers" not in kw
     
    -    def test_codex_backend_strips_caller_extra_headers(self, transport):
    +    def test_codex_backend_preserves_caller_extra_headers(self, transport):
             messages = [{"role": "user", "content": "Hi"}]
     
             kw = transport.build_kwargs(
    ```

    ```diff
    @@ -180,7 +196,10 @@ def test_codex_backend_strips_caller_extra_headers(self, transport):
                 request_overrides={"extra_headers": {"x-test": "1"}},
             )
     
    -        assert "extra_headers" not in kw
    +        headers = kw.get("extra_headers", {})
    +        assert headers.get("x-test") == "1"
    +        assert headers.get("session_id") == "conv-codex-1"
    +        assert headers.get("x-client-request-id") == "conv-codex-1"
     
         def test_non_codex_responses_preserves_caller_extra_headers(self, transport):
             messages = [{"role": "user", "content": "Hi"}]
    ```
- **#26749: fix(codex): drop extra_headers injection for chatgpt.com Codex backend (#26599)**. closed without merge
  - maintainer direction: category `merged_via`; implication `superseded`; refs `4fd9397ae`, `#45633`
- **#26767: fix(codex): avoid extra_headers on Codex backend**. closed without merge
  - maintainer direction: category `implemented_elsewhere`; implication `superseded`; refs `#45633`, `#26749`, `#26599`, `#45633`

## Fix status

- Merged to main in PR #45633 (cited above). No release data is tracked. Verify downstream.
- Fix last verified (merged) `2026-06-17`.
- Subsystem: `agent`
