{"record":{"id":"e335a594dfc4e8ef","repo":"unclecode/crawl4ai","slug":"unknown-hook-action-action-r-allowed-sorted-h","errorCode":null,"errorMessage":"unknown hook action {action!r}; allowed: {sorted(HOOK_REGISTRY)}","messagePattern":"unknown hook action (.+?); allowed: (.+?)","errorType":"validation","errorClass":"HookValidationError","httpStatus":400,"severity":"error","filePath":"deploy/docker/hook_registry.py","lineNumber":213,"sourceCode":"def build_declarative_hooks(specs: List[Any]) -> Dict[str, Callable]:\n    \"\"\"Validate declarative hook specs and return {hook_point: composed async hook}.\n\n    Each spec is an object/dict with `action` and `params`. Multiple specs that\n    target the same hook point are composed and run in order. Raises\n    HookValidationError on an unknown action or invalid params.\n    \"\"\"\n    if not specs:\n        return {}\n    if len(specs) > 10:\n        raise HookValidationError(\"too many hooks (max 10)\")\n\n    grouped: Dict[str, List[Callable]] = {}\n    for spec in specs:\n        action = spec.get(\"action\") if isinstance(spec, dict) else getattr(spec, \"action\", None)\n        raw_params = (spec.get(\"params\", {}) if isinstance(spec, dict) else getattr(spec, \"params\", {})) or {}\n        entry = HOOK_REGISTRY.get(action)\n        if entry is None:\n            raise HookValidationError(\n                f\"unknown hook action {action!r}; allowed: {sorted(HOOK_REGISTRY)}\"\n            )\n        try:\n            params = entry[\"params_model\"](**raw_params)\n        except Exception as e:\n            raise HookValidationError(f\"invalid params for hook '{action}': {e}\")\n        sub_hook = entry[\"factory\"](params)\n        grouped.setdefault(entry[\"hook_point\"], []).append(sub_hook)\n\n    hooks: Dict[str, Callable] = {}\n    for hook_point, sub_hooks in grouped.items():\n        def _compose(sub_hooks):\n            async def composed(page, **kwargs):\n                for fn in sub_hooks:\n                    await fn(page, **kwargs)\n                return page\n            return composed\n        hooks[hook_point] = _compose(sub_hooks)","sourceCodeStart":195,"sourceCodeEnd":231,"githubUrl":"https://github.com/unclecode/crawl4ai/blob/7e801521428ee12509994d39151006f64055ebe3/deploy/docker/hook_registry.py#L195-L231","documentation":"build_declarative_hooks raises HookValidationError('unknown hook action ...') when a spec's action string is not a key in HOOK_REGISTRY. The message includes the requested action and the sorted list of registered actions, so the supported vocabulary is self-describing.","triggerScenarios":"Sending {\"action\": \"wait_for_selector\", ...} or {\"action\": \"BlockResources\", ...} (wrong case) or a spec missing the action key (action=None) when HOOK_REGISTRY has no such entry.","commonSituations":"Version drift: the action existed in an older/newer deploy but not this one; case or naming mismatches (snake_case vs CamelCase); typos; specs authored against different hook registry forks.","solutions":["Read the error message: it prints allowed: sorted(HOOK_REGISTRY) — use one of those exact strings.","Check action spelling and case (actions are snake_case, e.g. block_resources, set_headers, add_cookies, scroll_to_bottom, wait_for_timeout).","Confirm the deployed image version matches the docs/API you authored the config against."],"exampleFix":"# before\n{\"action\": \"blockResources\", \"params\": {...}}\n\n# after\n{\"action\": \"block_resources\", \"params\": {...}}","handlingStrategy":"validation","validationCode":"from hook_registry import HOOK_REGISTRY\n\ndef valid_actions(specs) -> bool:\n    return all(\n        (s.get(\"action\") if isinstance(s, dict) else getattr(s, \"action\", None)) in HOOK_REGISTRY\n        for s in (specs or [])\n    )","typeGuard":"def is_known_hook_action(action) -> bool:\n    return isinstance(action, str) and action in HOOK_REGISTRY","tryCatchPattern":"try:\n    hooks = build_declarative_hooks(specs)\nexcept HookValidationError as e:\n    # message includes allowed: sorted(HOOK_REGISTRY)\n    raise ConfigError(str(e)) from e","preventionTips":["Use action strings verbatim from HOOK_REGISTRY keys; snake_case.","Pin the deployed image version and author configs against its registry.","Validate specs against HOOK_REGISTRY in CI before deploy."],"tags":["validation","hooks","config","api-contract"],"backgroundTag":null,"analyzedSha":"7e801521428ee12509994d39151006f64055ebe3","analyzedAt":"2026-08-14T20:46:20.673Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}