deepset-ai/haystack · error · TypeError
Hook registered for hook point '{hook_point}' is callable bu
Error message
Hook registered for hook point '{hook_point}' is callable but is not a Hook object. If it is a function, wrap it with the @hook decorator. What it means
After successful YAML parsing in _parse_frontmatter, the loaded value must be a mapping (dict). This ValueError is raised when the frontmatter parses to a non-dict (e.g. a YAML list, scalar string, or null). Skill frontmatter is required to be key/value metadata, so any other YAML root type is rejected.
Source
Thrown at haystack/components/agents/agent.py:129
def _validate_hooks(hooks: dict[HookPoint, list[Hook]]) -> None:
"""
Validate a hooks mapping: known hook points, real Hook objects, and hook-point restrictions.
:param hooks: Mapping of hook point to the hooks registered under it.
:raises ValueError: If a hook point is unknown, or a hook is registered under a hook point it does not support.
:raises TypeError: If a registered hook has no callable `run(state)`.
"""
for hook_point, hook_list in hooks.items():
if hook_point not in VALID_HOOK_POINTS:
raise ValueError(
f"Invalid hook point '{hook_point}'. Valid hook points are: {', '.join(VALID_HOOK_POINTS)}."
)
for h in hook_list:
if not callable(getattr(h, "run", None)):
if callable(h):
raise TypeError(
f"Hook registered for hook point '{hook_point}' is callable but is not a Hook object. "
"If it is a function, wrap it with the @hook decorator."
)
raise TypeError(
f"Hook registered for hook point '{hook_point}' must have a callable 'run(state)', "
f"got an object of type '{type(h).__name__}'."
)
# A hook may declare `allowed_hook_points` to restrict where it can run (e.g. ConfirmationHook only
# makes sense at "before_tool"). Hooks without it can be registered under any hook point.
allowed_points = getattr(h, "allowed_hook_points", None)
if allowed_points is not None and hook_point not in allowed_points:
raise ValueError(
f"Hook of type '{type(h).__name__}' is registered under hook point '{hook_point}' but only "
f"supports: {', '.join(allowed_points)}."
)
def _consume_continue_run(state: State) -> bool:View on GitHub (pinned to e318778c9b)
Solutions
- Rewrite the frontmatter between the '---' lines as key: value pairs (name, description).
- Check that the first '---' is on line 1 and a second '---' closes the block, so only the mapping is parsed.
- Test the block with yaml.safe_load and confirm it returns a dict.
Example fix
# before --- - name: my-skill - description: does stuff --- # after --- name: my-skill description: does stuff ---
Defensive patterns
Strategy: validation
Validate before calling
import yaml
from pathlib import Path
def frontmatter_is_mapping(skill_file: Path) -> bool:
lines = skill_file.read_text(encoding="utf-8").splitlines()
closing = lines.index("---", 1)
block = "\n".join(lines[1:closing])
loaded = yaml.safe_load(block) or {}
return isinstance(loaded, dict) and "description" in loaded Type guard
def is_frontmatter_mapping(loaded) -> bool:
return isinstance(loaded, dict) Try / catch
try:
store.load_skill(name)
except ValueError as e:
if "must be a YAML mapping" in str(e):
logger.error("Frontmatter of %s must be key: value pairs", name)
raise Prevention
- Author frontmatter strictly as key: value pairs, never lists or bare scalars
- Verify delimiters: first '---' on line 1, second '---' closes the block
- Add a CI check asserting yaml.safe_load(frontmatter) is a dict with required keys
When it happens
Trigger: Frontmatter block that is a YAML sequence (lines starting with '-'), a bare scalar (just a word or number), or content that collapses to None/non-dict, e.g. malformed delimiters causing the wrong region to be parsed.
Common situations: Deleting all keys but leaving '---' lines; accidental indentation turning keys into a nested list; a stray '-' bullet inside frontmatter; delimiters mis-detected so body text is parsed as frontmatter.
Related errors
- Invalid hook point '{hook_point}'. Valid hook points are: {'
- Hook of type '{type(h).__name__}' is registered under hook p
- Skill frontmatter is opened with '---' but never closed with
- Hook registered for hook point '{hook_point}' must have a ca
- {type(chat_generator).__name__} does not accept tools parame
AI-assisted analysis of deepset-ai/haystack@e318778c9b (2026-08-30).
Data as JSON: /api/errors/cef3027cc2398b94.
Report an issue: GitHub.