psf/requests · error · InvalidHeader
Invalid leading whitespace, reserved character(s), or…
Error message
Invalid leading whitespace, reserved character(s), or return character(s) in header {header_kind}: {header_part!r} What it means
Raised by _validate_header_part() when a header name or value contains characters that fail the validator regex: leading whitespace (for values), colons or whitespace (for names), or CR/LF characters anywhere. This is primarily a security guard against HTTP header injection (CRLF injection) attacks.
Solutions
- Strip CR/LF and leading whitespace from all header values before passing to requests: v.replace('\r', '').replace('\n', '').strip().
- Sanitize user input before placing it in headers: reject or escape any control characters.
- Use a header sanitization function for any data sourced from external input.
Example fix
# before
requests.get(url, headers={'X-User': user_input}) # user_input = 'alice\r\nX-Admin: true'
# after — sanitize
def safe_header(v):
return str(v).replace('\r', '').replace('\n', '').strip()
headers = {k: safe_header(v) for k, v in raw_headers.items()}
requests.get(url, headers=headers) Defensive patterns
Strategy: validation
Validate before calling
import re
def sanitize_header(value):
v = str(value).replace('\r', '').replace('\n', '').strip()
if re.search(r'[\x00-\x1f]', v):
raise ValueError(f'Header contains control characters: {v!r}')
return v
clean_headers = {k: sanitize_header(v) for k, v in headers.items()}
resp = requests.get(url, headers=clean_headers) Type guard
import re
def is_safe_header_value(v) -> bool:
if not isinstance(v, (str, bytes)):
return False
s = v if isinstance(v, str) else v.decode('ascii', errors='replace')
return not re.search(r'[\r\n]', s) and (not s or not s[0].isspace()) Try / catch
try:
resp = requests.get(url, headers=headers)
except requests.exceptions.InvalidHeader as e:
logger.error(f'Header validation failed: {e}')
clean = {k: str(v).replace('\r', '').replace('\n', '').strip()
for k, v in headers.items()}
resp = requests.get(url, headers=clean) Prevention
- Never place raw user input into HTTP headers without sanitizing CR/LF characters.
- Strip whitespace and control characters from all header values before passing to requests.
- Treat header construction from external data as a security boundary (header injection risk).
- Validate header names don't contain colons or whitespace.
When it happens
Trigger: Header value containing '\r\n' or '\n' (newline injection); header name with a colon; header value with leading space; header value containing raw return characters from user input or multi-line strings.
Common situations: User-supplied data placed into headers without sanitization (security vulnerability); copy-pasting multi-line strings into header values; building headers from log lines or file contents that contain newlines; deliberate header injection attempts from malicious input.
Related errors
- Header part ( ) from must be of type str or bytes, not
- chunk_size must be an int, it is instead a
- Cookie headers should be added with…
- No connection adapters were found for
AI-assisted analysis of psf/requests@8068356288 (2026-08-11).
Data as JSON: /api/errors/b89494c0d9bf600f.
Report an issue: GitHub.
Appendix: source
Thrown at src/requests/utils.py:1116
def _validate_header_part(
header: tuple[str | bytes, str | bytes],
header_part: str | bytes,
header_validator_index: int,
) -> None:
if isinstance(header_part, str):
validator = _HEADER_VALIDATORS_STR[header_validator_index]
elif isinstance(header_part, bytes):
# runtime guard for non-str/bytes input
validator = _HEADER_VALIDATORS_BYTE[header_validator_index]
else:
raise InvalidHeader(
f"Header part ({header_part!r}) from {header} "
f"must be of type str or bytes, not {type(header_part)}"
)
if not validator.match(header_part): # type: ignore[arg-type]
header_kind = "name" if header_validator_index == 0 else "value"
raise InvalidHeader(
f"Invalid leading whitespace, reserved character(s), or return "
f"character(s) in header {header_kind}: {header_part!r}"
)
def urldefragauth(url: str) -> str:
"""
Given a url remove the fragment and the authentication part.
:rtype: str
"""
scheme, netloc, path, params, query, _fragment = urlparse(url)
# see func:`prepend_scheme_if_needed`
if not netloc:
netloc, path = path, netloc
netloc = netloc.rsplit("@", 1)[-1]View on GitHub (pinned to 8068356288)