sipeed/picoclaw · error
build builtin hook %q: %w
Error message
build builtin hook %q: %w
What it means
The factory for an enabled builtin hook ran but rejected its spec: the hooks.builtins.<name>.config JSON blob failed that hook's own validation. The factory's error is wrapped with the hook name, so the inner message names the exact field or value the hook rejected. Initialization aborts and no further hooks mount.
Source
Thrown at pkg/agent/hook_mount.go:152
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)
for _, name := range processNames {
spec := al.cfg.Hooks.Processes[name]
opts, buildErr := processHookOptionsFromConfig(spec)
if buildErr != nil {
return fmt.Errorf("configure process hook %q: %w", name, buildErr)View on GitHub (pinned to 49183d7e8d)
Solutions
- Read the wrapped error after the hook name — it identifies the invalid field/value
- Check the hook provider's documentation for the expected config schema and correct the JSON
- Validate the config fragment parses (json.Unmarshal into the hook's options struct) in a scratch program or CI
- After upgrading the hook package, re-check for renamed fields
Example fix
// before — threshold passed as string
"builtins": { "audit_spell": { "enabled": true, "config": { "threshold": "0.8" } } }
// after — correct type
"builtins": { "audit_spell": { "enabled": true, "config": { "threshold": 0.8 } } } Defensive patterns
Strategy: try-catch
Validate before calling
for name, spec := range cfg.Hooks.Builtins {
if !spec.Enabled || len(spec.Config) == 0 {
continue
}
var probe any
if err := json.Unmarshal(spec.Config, &probe); err != nil {
return fmt.Errorf("hooks.builtins.%s: config is not valid JSON: %w", name, err)
}
} Try / catch
if err := al.EnsureHooksInitialized(ctx); err != nil {
if hookErr, ok := unwrapPrefix(err, "build builtin hook"); ok {
// config/schema problem: fix config and restart; do not retry blindly
log.Printf("hook config rejected: %v", hookErr)
}
} Prevention
- Unmarshal each hook's config JSON into the hook's options struct in a config-lint step
- Pin hook extension versions so config schemas cannot drift under you
- Prefer small config surfaces; unknown extra keys usually mean nesting mistakes
When it happens
Trigger: hooks.builtins.<name>.enabled=true with a config object whose fields are missing, wrong-typed, or out of range for that hook's schema (each RegisterBuiltinHook factory defines its own).
Common situations: Hand-editing hook config JSON; schema drift after upgrading the package providing the hook; nesting settings one level too deep/shallow; passing strings where numbers are expected.
Related errors
- configure process hook %q: %w
- command is required
- tool discovery is enabled but neither 'use_bm25' nor 'use_re
- builtin hook %q is not registered
- unsupported transport %q
AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15).
Data as JSON: /api/errors/f5ecf8ecb57b7c60.
Report an issue: GitHub.