{"record":{"id":"2d3c53d6f6a320b1","repo":"sipeed/picoclaw","slug":"start-process-hook-q-w","errorCode":null,"errorMessage":"start process hook %q: %w","messagePattern":"start process hook %q: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/agent/hook_mount.go","lineNumber":175,"sourceCode":"\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\n\treturn nil\n}\n\nfunc enabledBuiltinHookNames(specs map[string]config.BuiltinHookConfig) []string {\n\tif len(specs) == 0 {","sourceCodeStart":157,"sourceCodeEnd":193,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/pkg/agent/hook_mount.go#L157-L193","documentation":"After config validation, NewProcessHook spawns the hook's command and speaks stdio to it. This wrapper means process startup failed — the binary could not be executed or the process died during startup/handshake. The hook name plus the wrapped exec error identify the culprit; the partially started hook is not mounted.","triggerScenarios":"hooks.processes.<name>.enabled=true with a command whose binary does not exist in PATH, is not executable, targets the wrong architecture, runs from a nonexistent dir, or exits immediately (missing interpreter like python/node, missing env).","commonSituations":"PATH differences under systemd/docker (works in shell, fails as a service); script without a shebang or without +x; relative dir that only exists in another cwd; missing env var the hook needs at boot.","solutions":["Run the exact command array from config in the same environment (same user, PATH, dir) to reproduce","Use an absolute path for command[0] instead of relying on PATH","chmod +x the script and ensure a shebang (or invoke the interpreter explicitly: [\"python\", \"/opt/hook.py\"])","Verify spec.dir exists and add required env entries under the hook's env map","Check the hook's own stderr/startup logs — an immediately exiting process surfaces here"],"exampleFix":"// before\n\"command\": [\"audit-hook\"]\n\n// after\n\"command\": [\"/usr/local/bin/audit-hook\"]\n// or for scripts:\n\"command\": [\"python3\", \"/opt/picoclaw/hooks/audit_hook.py\"]","handlingStrategy":"validation","validationCode":"for name, p := range cfg.Hooks.Processes {\n    if !p.Enabled || len(p.Command) == 0 {\n        continue\n    }\n    if filepath.IsAbs(p.Command[0]) {\n        if _, err := os.Stat(p.Command[0]); err != nil {\n            return fmt.Errorf(\"hooks.processes.%s: binary missing: %w\", name, err)\n        }\n    } else if _, err := exec.LookPath(p.Command[0]); err != nil {\n        return fmt.Errorf(\"hooks.processes.%s: %s not in PATH: %w\", name, p.Command[0], err)\n    }\n    if p.Dir != \"\" {\n        if _, err := os.Stat(p.Dir); err != nil {\n            return fmt.Errorf(\"hooks.processes.%s: dir missing: %w\", name, err)\n        }\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Use absolute paths for hook binaries — service managers and containers have different PATH","Add the hook's runtime (python/node) to the image and verify with exec.LookPath in CI","Log hook stderr to a file so immediately-exiting processes are diagnosable"],"tags":["go","picoclaw","hooks","process-hooks","exec","environment"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}