psf/requests · warning · NotImplementedError

Cookie headers should be added with…

Error message

Cookie headers should be added with add_unredirected_header()

What it means

Raised by MockRequest.add_header (the shim used by requests.cookies to bridge to urllib3/http.cookiejar). The cookiejar machinery calls add_header for certain operations, but requests manages cookie headers exclusively via add_unredirected_header, so this method intentionally refuses. It exists purely to redirect maintainers to the correct API.

Solutions

  1. Call add_unredirected_header(name, value) instead of add_header(key, val) when adding cookie headers to a requests MockRequest.
  2. If you are managing cookies at the user level, set them via the session's cookies jar (session.cookies.set(name, value)) or pass the cookies= dict to request(), which routes through the correct API.
  3. If the call comes from inside cookiejar internals, report it upstream; the message itself invites you to add the method back if you find a legitimate use.

Example fix

// before
mock_request.add_header('Cookie', 'sid=abc')

// after
mock_request.add_unredirected_header('Cookie', 'sid=abc')
Defensive patterns

Strategy: validation

Validate before calling

# This is internal API; user-level code should not call add_header on the MockRequest.
# Validate at the cookie-management level instead:
from http.cookiejar import CookieJar

def safe_set_cookie(jar, name, value, domain=None, path='/'):
    assert isinstance(jar, CookieJar)
    jar.set_cookie(create_cookie(name=name, value=value, domain=domain, path=path))

Type guard

# Low-level guard if you must handle a MockRequest-like object:
from typing import Protocol

class HasUnredirectedHeader(Protocol):
    def add_unredirected_header(self, name: str, value: str) -> None: ...

def is_mock_request(obj) -> bool:
    return hasattr(obj, 'add_unredirected_header') and hasattr(obj, 'get_new_headers')

Try / catch

# Not typically caught; prefer to avoid calling add_header at all.
# If integrating with code that may call add_header, wrap it:
try:
    obj.add_header(k, v)
except NotImplementedError:
    obj.add_unredirected_header(k, v)

Prevention

When it happens

Trigger: Calling .add_header(key, val) on the MockRequest object that requests wraps around each PreparedRequest when feeding cookies through a CookieJar; internal cookiejar flows that route through add_header when the cookie policy triggers a header-set path.

Common situations: Monkey-patching or instrumenting cookie handling and accidentally hitting add_header; downstream libraries that operate on http.cookiejar.CookieJar abstractions and assume add_header works; a fork or fork-equivalent that wires its own cookie policy.

Related errors


AI-assisted analysis of psf/requests@8068356288 (2026-08-11). Data as JSON: /api/errors/3154592a1005157a. Report an issue: GitHub.

Appendix: source

Thrown at src/requests/cookies.py:91

                parsed.path,
                parsed.params,
                parsed.query,
                parsed.fragment,
            ]
        )

    def is_unverifiable(self) -> bool:
        return True

    def has_header(self, name: str) -> bool:
        return name in self._r.headers or name in self._new_headers

    def get_header(self, name: str, default: str | None = None) -> str | None:
        return self._r.headers.get(name, self._new_headers.get(name, default))  # type: ignore[return-value]

    def add_header(self, key: str, val: str) -> None:
        """cookiejar has no legitimate use for this method; add it back if you find one."""
        raise NotImplementedError(
            "Cookie headers should be added with add_unredirected_header()"
        )

    def add_unredirected_header(self, name: str, value: str) -> None:
        self._new_headers[name] = value

    def get_new_headers(self) -> dict[str, str]:
        return self._new_headers

    @property
    def unverifiable(self) -> bool:
        return self.is_unverifiable()

    @property
    def origin_req_host(self) -> str:
        return self.get_origin_req_host()

    @property

View on GitHub (pinned to 8068356288)