{"record":{"id":"ff7a0243d0c55d22","repo":"deepset-ai/haystack","slug":"invalid-hook-point-hook-point-valid-hook-poin","errorCode":null,"errorMessage":"Invalid hook point '{hook_point}'. Valid hook points are: {', '.join(VALID_HOOK_POINTS)}.","messagePattern":"Invalid hook point '(.+?)'\\. Valid hook points are: (.+?)\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"haystack/components/agents/agent.py","lineNumber":123,"sourceCode":"    return {name for name, p in sig.parameters.items() if p.kind != inspect.Parameter.VAR_KEYWORD}\n\n\ndef _public_outputs(state: State) -> dict[str, Any]:\n    \"\"\"Return the State data excluding the internal state keys (i.e. the Agent's user-facing outputs).\"\"\"\n    return {key: value for key, value in state.data.items() if key not in _INTERNAL_STATE_KEYS}\n\n\ndef _validate_hooks(hooks: dict[HookPoint, list[Hook]]) -> None:\n    \"\"\"\n    Validate a hooks mapping: known hook points, real Hook objects, and hook-point restrictions.\n\n    :param hooks: Mapping of hook point to the hooks registered under it.\n    :raises ValueError: If a hook point is unknown, or a hook is registered under a hook point it does not support.\n    :raises TypeError: If a registered hook has no callable `run(state)`.\n    \"\"\"\n    for hook_point, hook_list in hooks.items():\n        if hook_point not in VALID_HOOK_POINTS:\n            raise ValueError(\n                f\"Invalid hook point '{hook_point}'. Valid hook points are: {', '.join(VALID_HOOK_POINTS)}.\"\n            )\n        for h in hook_list:\n            if not callable(getattr(h, \"run\", None)):\n                if callable(h):\n                    raise TypeError(\n                        f\"Hook registered for hook point '{hook_point}' is callable but is not a Hook object. \"\n                        \"If it is a function, wrap it with the @hook decorator.\"\n                    )\n                raise TypeError(\n                    f\"Hook registered for hook point '{hook_point}' must have a callable 'run(state)', \"\n                    f\"got an object of type '{type(h).__name__}'.\"\n                )\n            # A hook may declare `allowed_hook_points` to restrict where it can run (e.g. ConfirmationHook only\n            # makes sense at \"before_tool\"). Hooks without it can be registered under any hook point.\n            allowed_points = getattr(h, \"allowed_hook_points\", None)\n            if allowed_points is not None and hook_point not in allowed_points:\n                raise ValueError(","sourceCodeStart":105,"sourceCodeEnd":141,"githubUrl":"https://github.com/deepset-ai/haystack/blob/e318778c9bf60a1963e3b5f451359655dd696c30/haystack/components/agents/agent.py#L105-L141","documentation":"FileSystemSkillStore parses YAML frontmatter from each skill's markdown file during warm_up/load_skill via _parse_frontmatter. This ValueError is raised when yaml.safe_load on the frontmatter block (content between --- delimiters) raises yaml.YAMLError, meaning the frontmatter is syntactically invalid YAML. The original YAMLError is chained so its message (with line/column info) is appended.","triggerScenarios":"Any skill .md file whose frontmatter block contains malformed YAML: unescaped colons in values, bad indentation, tabs instead of spaces, unclosed quotes or brackets, or a stray '---' split that puts non-YAML content into the block.","commonSituations":"Hand-edited SKILL.md files; copying a skill with a broken description containing ': ' or '#' unquoted; YAML tabs; editing on Windows leaving stray characters; a stray '---' line inside the skill text that mis-slices the block.","solutions":["Open the skill .md file named in the chained YAMLError message and fix the YAML syntax at the reported line/column (quote values containing colons, use spaces not tabs).","Verify the file has exactly two '---' delimiter lines and that only YAML sits between them.","Validate the frontmatter with python -c \"import yaml,sys; yaml.safe_load(open('SKILL.md').read().split('---')[1])\" before retrying."],"exampleFix":"# before (SKILL.md frontmatter)\n---\nname: my-skill\ndescription: Use this when: something happens\n---\n# after\n---\nname: my-skill\ndescription: \"Use this when: something happens\"\n---","handlingStrategy":"validation","validationCode":"import yaml\nfrom pathlib import Path\n\ndef frontmatter_is_valid_yaml(skill_file: Path) -> bool:\n    lines = skill_file.read_text(encoding=\"utf-8\").splitlines()\n    if not lines or lines[0].strip() != \"---\":\n        return False\n    try:\n        closing = lines.index(\"---\", 1)\n    except ValueError:\n        return False\n    try:\n        yaml.safe_load(\"\\n\".join(lines[1:closing])) or {}\n        return True\n    except yaml.YAMLError:\n        return False","typeGuard":"def is_valid_frontmatter_block(block: str) -> bool:\n    try:\n        return isinstance(yaml.safe_load(block) or {}, dict)\n    except yaml.YAMLError:\n        return False","tryCatchPattern":"try:\n    store.load_skill(name)\nexcept ValueError as e:\n    if \"not valid YAML\" in str(e):\n        logger.error(\"Fix YAML in skill file: %s\", e)\n    raise","preventionTips":["Quote frontmatter values containing ':', '#', or '{' characters","Use spaces, never tabs, for YAML indentation","Lint all skill .md frontmatter with yamllint in CI before shipping","Keep exactly two '---' delimiter lines and no stray ones in the body"],"tags":["yaml","skill-store","frontmatter","parsing"],"backgroundTag":"invalid-yaml-frontmatter","analyzedSha":"e318778c9bf60a1963e3b5f451359655dd696c30","analyzedAt":"2026-08-30T11:45:20.711Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}