unclecode/crawl4ai · warning · ValueError

invalid webhook header name: {name!r}

Error message

invalid webhook header name: {name!r}

What it means

ValueError from sanitize_webhook_headers when a header name is not a string or fails the _WEBHOOK_HEADER_NAME regex (valid RFC 7230 token characters only). The offending name is echoed in quotes for debugging.

Source

Thrown at deploy/docker/webhook.py:72

_WEBHOOK_DENY_HEADERS = {
    "host", "content-length", "transfer-encoding", "connection",
    "content-type", "proxy-authorization", "authorization", "cookie",
    "expect", "upgrade", "te", "trailer",
}
_MAX_WEBHOOK_HEADERS = 20
_MAX_WEBHOOK_HEADER_VALUE = 2048


def sanitize_webhook_headers(headers: Optional[Dict[str, str]]) -> Dict[str, str]:
    """Validate user-supplied webhook headers; raise ValueError on any bad one."""
    if not headers:
        return {}
    if len(headers) > _MAX_WEBHOOK_HEADERS:
        raise ValueError("too many webhook headers")
    clean: Dict[str, str] = {}
    for name, value in headers.items():
        if not isinstance(name, str) or not _WEBHOOK_HEADER_NAME.match(name):
            raise ValueError(f"invalid webhook header name: {name!r}")
        if name.lower() in _WEBHOOK_DENY_HEADERS:
            raise ValueError(f"webhook header not allowed: {name}")
        sval = str(value)
        if len(sval) > _MAX_WEBHOOK_HEADER_VALUE or any(c in sval for c in "\r\n\x00"):
            raise ValueError(f"invalid value for webhook header {name}")
        clean[name] = sval
    return clean


class WebhookDeliveryService:
    """Handles webhook delivery with exponential backoff retry logic."""

    def __init__(self, config: Dict):
        """
        Initialize the webhook delivery service.

        Args:
            config: Application configuration dictionary containing webhook settings

View on GitHub (pinned to 7e80152142)

Solutions

  1. Use standard token names: letters, digits, and hyphens (e.g. X-Custom-Header)
  2. Sanitize on the client: re.sub(r'[^A-Za-z0-9_-]', '-', name) before sending
  3. Assert len(name) > 0 and name == name.strip() when generating names dynamically

Example fix

# before
"headers": {"X Crawl Source": "batch-1"}

# after
"headers": {"X-Crawl-Source": "batch-1"}
Defensive patterns

Strategy: validation

Validate before calling

import re
TOKEN = re.compile(r"^[A-Za-z0-9!#$%&'*+.^_`|~-]+$")

def valid_header_names(headers: dict) -> bool:
    return all(isinstance(k, str) and TOKEN.match(k) for k in headers)

Type guard

def is_valid_webhook_header_set(h) -> bool:
    return (isinstance(h, dict) and len(h) <= 20
            and all(isinstance(k, str) and TOKEN.match(k) for k in h))

Try / catch

try:
    sanitize_webhook_headers(headers)
except ValueError as e:
    if "header name" in str(e):
        headers = {re.sub(r"[^A-Za-z0-9-]", "-", k): v for k, v in headers.items()}

Prevention

When it happens

Trigger: A webhook headers key containing spaces ('X My Header'), non-ASCII, brackets, or empty string; or a non-str key (int, None) coming from JSON with odd keys or programmatic dict building.

Common situations: Client copies browser request headers including formatting quirks; using 'X-Custom Header:' style labels; keys generated from f-strings with stray whitespace or newlines.

Related errors


AI-assisted analysis of unclecode/crawl4ai@7e80152142 (2026-08-14). Data as JSON: /api/errors/bddeb81890b97f15. Report an issue: GitHub.