{"record":{"id":"630ace8b785a033c","repo":"sipeed/picoclaw","slug":"configure-process-hook-q-w","errorCode":null,"errorMessage":"configure process hook %q: %w","messagePattern":"configure process hook %q: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/agent/hook_mount.go","lineNumber":170,"sourceCode":"\t\t\treturn fmt.Errorf(\"build builtin hook %q: %w\", name, factoryErr)\n\t\t}\n\t\tif err := al.MountHook(HookRegistration{\n\t\t\tName:     name,\n\t\t\tPriority: spec.Priority,\n\t\t\tSource:   HookSourceInProcess,\n\t\t\tHook:     hook,\n\t\t}); err != nil {\n\t\t\treturn fmt.Errorf(\"mount builtin hook %q: %w\", name, err)\n\t\t}\n\t\tmounted = append(mounted, name)\n\t}\n\n\tprocessNames := enabledProcessHookNames(al.cfg.Hooks.Processes)\n\tfor _, name := range processNames {\n\t\tspec := al.cfg.Hooks.Processes[name]\n\t\topts, buildErr := processHookOptionsFromConfig(spec)\n\t\tif buildErr != nil {\n\t\t\treturn fmt.Errorf(\"configure process hook %q: %w\", name, buildErr)\n\t\t}\n\n\t\tprocessHook, buildErr := NewProcessHook(ctx, name, opts)\n\t\tif buildErr != nil {\n\t\t\treturn fmt.Errorf(\"start process hook %q: %w\", name, buildErr)\n\t\t}\n\t\tif err := al.MountHook(HookRegistration{\n\t\t\tName:     name,\n\t\t\tPriority: spec.Priority,\n\t\t\tSource:   HookSourceProcess,\n\t\t\tHook:     processHook,\n\t\t}); err != nil {\n\t\t\t_ = processHook.Close()\n\t\t\treturn fmt.Errorf(\"mount process hook %q: %w\", name, err)\n\t\t}\n\t\tmounted = append(mounted, name)\n\t}\n","sourceCodeStart":152,"sourceCodeEnd":188,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/pkg/agent/hook_mount.go#L152-L188","documentation":"Every enabled entry in hooks.processes is validated by processHookOptionsFromConfig before anything launches. This wrapper means validation failed for that hook: transport is not 'stdio', command is empty, an observe entry is not a known event kind, an intercept entry is unsupported, or no hook mode is enabled at all. The unwrapped message says which constraint failed.","triggerScenarios":"A hooks.processes.<name> entry with enabled=true and any of: transport set to something other than \"stdio\" (empty is fine), command missing/empty, observe listing invalid kinds, intercept outside {before_llm, after_llm, before_tool, after_tool, approve_tool}, or all of observe/intercept left empty ('no hook modes enabled').","commonSituations":"Assuming network transports (http/grpc) exist for hooks like they do for MCP; enabling a process hook before writing its command; event-kind typos like 'tool_start' instead of 'tool_exec_start'; forgetting that a process hook must observe or intercept something.","solutions":["Read the unwrapped inner message — it names the exact failed constraint","Set transport to \"stdio\" or remove the field (it is the default)","Provide command as a non-empty argv array","Use valid observe kinds (runtime event names such as tool_exec_start, turn_end, or '*' for all)","Enable at least one mode: observe non-empty or intercept containing before_llm/after_llm/before_tool/after_tool/approve_tool"],"exampleFix":"// before\n\"processes\": { \"audit\": { \"enabled\": true, \"transport\": \"grpc\",\n  \"command\": [], \"intercept\": [\"before_llm\"] } }\n\n// after\n\"processes\": { \"audit\": { \"enabled\": true,\n  \"command\": [\"/usr/local/bin/audit-hook\"],\n  \"intercept\": [\"before_llm\"] } }","handlingStrategy":"validation","validationCode":"validIntercept := map[string]bool{\"before_llm\": true, \"after_llm\": true, \"before_tool\": true, \"after_tool\": true, \"approve_tool\": true}\nfor name, p := range cfg.Hooks.Processes {\n    if !p.Enabled {\n        continue\n    }\n    if p.Transport != \"\" && p.Transport != \"stdio\" {\n        return fmt.Errorf(\"hooks.processes.%s: only stdio transport supported\", name)\n    }\n    if len(p.Command) == 0 {\n        return fmt.Errorf(\"hooks.processes.%s: command required\", name)\n    }\n    hasMode := len(p.Observe) > 0 || len(p.Intercept) > 0\n    for _, it := range p.Intercept {\n        if !validIntercept[it] {\n            return fmt.Errorf(\"hooks.processes.%s: bad intercept %q\", name, it)\n        }\n    }\n    if !hasMode {\n        return fmt.Errorf(\"hooks.processes.%s: enable observe or intercept\", name)\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Run this validation as a config lint before deploy — all sub-errors (transport/command/observe/intercept/modes) are caught here","Use event names from the runtime events list (e.g. tool_exec_start) or '*' for observe","Remember a process hook with neither observe nor intercept is always rejected"],"tags":["go","picoclaw","hooks","process-hooks","config","validation"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}