{"record":{"id":"6f6caf459cb0598d","repo":"unclecode/crawl4ai","slug":"invalid-header-name-name-r","errorCode":null,"errorMessage":"invalid header name {name!r}","messagePattern":"invalid header name (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":400,"severity":"error","filePath":"deploy/docker/hook_registry.py","lineNumber":76,"sourceCode":"    secure: bool = True\n    httpOnly: bool = False\n\n\nclass AddCookiesParams(BaseModel):\n    cookies: List[_Cookie] = Field(..., min_length=1, max_length=_MAX_COOKIES)\n\n\nclass SetHeadersParams(BaseModel):\n    headers: Dict[str, str]\n\n    @field_validator(\"headers\")\n    @classmethod\n    def _check(cls, v):\n        if len(v) > _MAX_HEADERS:\n            raise ValueError(f\"too many headers (max {_MAX_HEADERS})\")\n        for name, value in v.items():\n            if not _HEADER_NAME_RE.match(name):\n                raise ValueError(f\"invalid header name {name!r}\")\n            if any(c in value for c in \"\\r\\n\\x00\"):\n                raise ValueError(f\"control characters in value for header {name!r}\")\n        return v\n\n\nclass ScrollToBottomParams(BaseModel):\n    max_steps: int = Field(10, ge=1, le=_MAX_SCROLL_STEPS)\n    delay_ms: int = Field(500, ge=0, le=_MAX_SCROLL_DELAY_MS)\n\n\nclass WaitForTimeoutParams(BaseModel):\n    timeout_ms: int = Field(..., ge=0, le=_MAX_WAIT_MS)\n\n\n# ───────────────────────── server-authored hook factories ─────────────────────────\ndef _factory_block_resources(p: BlockResourcesParams):\n    types = set(p.resource_types)\n","sourceCodeStart":58,"sourceCodeEnd":94,"githubUrl":"https://github.com/unclecode/crawl4ai/blob/7e801521428ee12509994d39151006f64055ebe3/deploy/docker/hook_registry.py#L58-L94","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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)."],"exampleFix":"# before\n{\"headers\": {\"User-Agent: Mobile\": \"UA\", \"X_Custom\": \"1\"}}\n\n# after\n{\"headers\": {\"User-Agent\": \"Mobile UA\", \"X-Custom\": \"1\"}}","handlingStrategy":"validation","validationCode":"import re\nHEADER_NAME_RE = re.compile(r\"^[A-Za-z0-9-]{1,64}$\")\n\ndef valid_header_names(spec: dict) -> bool:\n    h = spec.get(\"params\", {}).get(\"headers\", {})\n    return all(HEADER_NAME_RE.match(k) for k in h)","typeGuard":"def is_http_token_name(name) -> bool:\n    return isinstance(name, str) and bool(re.fullmatch(r\"[A-Za-z0-9-]{1,64}\", name))","tryCatchPattern":"try:\n    hooks = build_declarative_hooks(specs)\nexcept HookValidationError as e:\n    raise ConfigError(str(e)) from e  # message names the offending header","preventionTips":["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-]."],"tags":["validation","http-headers","hooks","security"],"backgroundTag":null,"analyzedSha":"7e801521428ee12509994d39151006f64055ebe3","analyzedAt":"2026-08-14T20:46:20.673Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}