{"record":{"id":"32fed0f979bcbdee","repo":"unclecode/crawl4ai","slug":"too-many-hooks-max-10","errorCode":null,"errorMessage":"too many hooks (max 10)","messagePattern":"too many hooks \\(max 10\\)","errorType":"validation","errorClass":"HookValidationError","httpStatus":400,"severity":"error","filePath":"deploy/docker/hook_registry.py","lineNumber":205,"sourceCode":"        \"hook_point\": \"before_retrieve_html\",\n        \"params_model\": WaitForTimeoutParams,\n        \"factory\": _factory_wait_for_timeout,\n        \"description\": \"Wait a bounded number of milliseconds before retrieving HTML.\",\n    },\n}\n\n\ndef 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] = {}","sourceCodeStart":187,"sourceCodeEnd":223,"githubUrl":"https://github.com/unclecode/crawl4ai/blob/7e801521428ee12509994d39151006f64055ebe3/deploy/docker/hook_registry.py#L187-L223","documentation":"build_declarative_hooks raises HookValidationError('too many hooks (max 10)') when the declarative spec list exceeds 10 entries. The cap bounds config size and per-page overhead since every hook runs on each matching page event.","triggerScenarios":"Posting a crawl config whose hooks array contains 11 or more {action, params} specs.","commonSituations":"Generated configs that enumerate per-domain hooks; accumulating hooks across merges/patches until the list grows past 10; teams unaware the limit counts ALL hooks, not per hook point.","solutions":["Consolidate specs: one block_resources spec accepts a list of resource types; one set_headers spec accepts up to 20 headers.","Delete hooks you no longer need rather than commenting them out in generated config.","If the workflow truly needs more, request a raise of the cap or run two crawl jobs."],"exampleFix":"# before\nhooks = [{\"action\": \"block_resources\", \"params\": {\"resource_types\": [\"image\"]}},\n          {\"action\": \"block_resources\", \"params\": {\"resource_types\": [\"font\"]}}, ...  # 11 specs\n\n# after\nhooks = [{\"action\": \"block_resources\", \"params\": {\"resource_types\": [\"image\", \"font\", \"media\", \"stylesheet\"]}}]","handlingStrategy":"validation","validationCode":"def valid_hook_count(specs) -> bool:\n    return isinstance(specs, list) and len(specs) <= 10","typeGuard":"def is_within_hook_cap(specs) -> bool:\n    return not specs or len(specs) <= 10","tryCatchPattern":"try:\n    hooks = build_declarative_hooks(specs)\nexcept HookValidationError as e:\n    if \"too many hooks\" in str(e):\n        specs = merge_same_action_specs(specs)  # e.g. fold block_resources lists\n    else:\n        raise","preventionTips":["Merge specs targeting the same action before submit.","Assert len(hooks) <= 10 in config-generation tests.","Delete obsolete hooks from templates instead of accumulating them."],"tags":["validation","hooks","config","limits"],"backgroundTag":null,"analyzedSha":"7e801521428ee12509994d39151006f64055ebe3","analyzedAt":"2026-08-14T20:46:20.673Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}