unclecode/crawl4ai · error · ValueError
invalid header name {name!r}
Error message
invalid header name {name!r} What it means
SetHeadersParams' validator enforces _HEADER_NAME_RE = ^[A-Za-z0-9-]{1,64}$ on every header name. Names with spaces, underscores, colons, unicode, or longer than 64 chars are rejected. This keeps header injection and malformed Chromium CDP headers out of declarative hook configs.
Source
Thrown at deploy/docker/hook_registry.py:76
secure: bool = True
httpOnly: bool = False
class AddCookiesParams(BaseModel):
cookies: List[_Cookie] = Field(..., min_length=1, max_length=_MAX_COOKIES)
class SetHeadersParams(BaseModel):
headers: Dict[str, str]
@field_validator("headers")
@classmethod
def _check(cls, v):
if len(v) > _MAX_HEADERS:
raise ValueError(f"too many headers (max {_MAX_HEADERS})")
for name, value in v.items():
if not _HEADER_NAME_RE.match(name):
raise ValueError(f"invalid header name {name!r}")
if any(c in value for c in "\r\n\x00"):
raise ValueError(f"control characters in value for header {name!r}")
return v
class ScrollToBottomParams(BaseModel):
max_steps: int = Field(10, ge=1, le=_MAX_SCROLL_STEPS)
delay_ms: int = Field(500, ge=0, le=_MAX_SCROLL_DELAY_MS)
class WaitForTimeoutParams(BaseModel):
timeout_ms: int = Field(..., ge=0, le=_MAX_WAIT_MS)
# ───────────────────────── server-authored hook factories ─────────────────────────
def _factory_block_resources(p: BlockResourcesParams):
types = set(p.resource_types)
View on GitHub (pinned to 7e80152142)
Solutions
- Use the exact HTTP token form: letters, digits, and hyphens only, e.g. 'User-Agent', 'X-Request-Id'.
- Strip any trailing ': value' when converting from curl syntax.
- Replace underscores with hyphens (X_Custom_Header -> X-Custom-Header).
Example fix
# before
{"headers": {"User-Agent: Mobile": "UA", "X_Custom": "1"}}
# after
{"headers": {"User-Agent": "Mobile UA", "X-Custom": "1"}} Defensive patterns
Strategy: validation
Validate before calling
import re
HEADER_NAME_RE = re.compile(r"^[A-Za-z0-9-]{1,64}$")
def valid_header_names(spec: dict) -> bool:
h = spec.get("params", {}).get("headers", {})
return all(HEADER_NAME_RE.match(k) for k in h) Type guard
def is_http_token_name(name) -> bool:
return isinstance(name, str) and bool(re.fullmatch(r"[A-Za-z0-9-]{1,64}", name)) Try / catch
try:
hooks = build_declarative_hooks(specs)
except HookValidationError as e:
raise ConfigError(str(e)) from e # message names the offending header Prevention
- Normalize names from curl syntax: split on first ':' and strip whitespace.
- Convert underscores to hyphens before submission.
- Add a lint rule rejecting header names outside [A-Za-z0-9-].
When it happens
Trigger: Passing 'User-Agent: Mobile' (colon included), 'X_Custom_Header' (underscore), 'Accept Encoding' (space), a non-ASCII header name, or a name over 64 characters in set_headers params.
Common situations: Copy-pasting headers from curl -H syntax ('Header: value') into the name field; using underscores because proxies like nginx historically accept them; HTTP/2 lowercase names are fine but separators must be hyphens.
Related errors
- control characters in value for header {name!r}
- webhook header not allowed: {name}
- LLMConfig.api_token may not reference an environment variabl
- Hook '{hook_name}' must be a callable function, got {type(ho
- Rejected request: {e}
AI-assisted analysis of unclecode/crawl4ai@7e80152142 (2026-08-14).
Data as JSON: /api/errors/6f6caf459cb0598d.
Report an issue: GitHub.