sipeed/picoclaw · error
builtin hook %q is not registered
Error message
builtin hook %q is not registered
What it means
At startup, every name enabled under hooks.builtins is looked up in an in-process registry populated by agent.RegisterBuiltinHook. picoclaw itself registers no builtins in the default build — they are an extension point for embeddings — so this error means config enables a builtin hook name that no RegisterBuiltinHook call in your binary provides (typo, renamed hook, or a missing side-effect import of the package that registers it).
Source
Thrown at pkg/agent/hook_mount.go:147
}
mounted := make([]string, 0)
defer func() {
if err != nil {
for _, name := range mounted {
al.UnmountHook(name)
}
return
}
al.hookRuntime.setMounted(mounted)
}()
builtinNames := enabledBuiltinHookNames(al.cfg.Hooks.Builtins)
for _, name := range builtinNames {
spec := al.cfg.Hooks.Builtins[name]
factory, ok := lookupBuiltinHook(name)
if !ok {
return fmt.Errorf("builtin hook %q is not registered", name)
}
hook, factoryErr := factory(ctx, spec)
if factoryErr != nil {
return fmt.Errorf("build builtin hook %q: %w", name, factoryErr)
}
if err := al.MountHook(HookRegistration{
Name: name,
Priority: spec.Priority,
Source: HookSourceInProcess,
Hook: hook,
}); err != nil {
return fmt.Errorf("mount builtin hook %q: %w", name, err)
}
mounted = append(mounted, name)
}
processNames := enabledProcessHookNames(al.cfg.Hooks.Processes)View on GitHub (pinned to 49183d7e8d)
Solutions
- List the names your binary actually registers (grep your code for RegisterBuiltinHook calls) and fix the config name to match
- Import the package that calls RegisterBuiltinHook for that hook so its init() runs before startup
- Update picoclaw/embedding to the version that provides the hook you configured
- Set enabled=false for the unknown entry if the hook is not needed
Example fix
// before — config enables a name nothing registers
"hooks": { "enabled": true, "builtins": { "auditspell": { "enabled": true } } }
// after — name matches the registration
import picoclawhooks "yourcorp/picoclaw-extra-hooks" // registers "audit_spell"
"hooks": { "enabled": true, "builtins": { "audit_spell": { "enabled": true } } } Defensive patterns
Strategy: validation
Validate before calling
registered := map[string]bool{
// keep in sync with every RegisterBuiltinHook call linked into the binary
"audit_spell": true,
}
for name, spec := range cfg.Hooks.Builtins {
if spec.Enabled && !registered[name] {
return fmt.Errorf("hooks.builtins.%s: no factory registered in this build", name)
}
} Prevention
- Import every package that calls RegisterBuiltinHook from your main package so registration side effects always run
- Maintain a name allowlist and validate hooks config against it in CI
- After upgrading picoclaw or hook extensions, diff the registered-hook list against config
When it happens
Trigger: hooks.enabled=true and hooks.builtins.<name>.enabled=true where <name> has no registered factory: misspelled name, a hook from a newer picoclaw version, or an extension package that registers the hook was never imported (so its init() never ran).
Common situations: Copying hooks config from docs of a different version; forks that dropped builtin hooks; forgetting that registration happens via package side effects, so dead-code elimination or a missing import silently drops the factory.
Related errors
- build builtin hook %q: %w
- configure process hook %q: %w
- unsupported transport %q
- command is required
- channel %s not found
AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15).
Data as JSON: /api/errors/9560647b139dbf97.
Report an issue: GitHub.