{"record":{"id":"800e6cc29dbd5e89","repo":"sipeed/picoclaw","slug":"mount-process-hook-q-w","errorCode":null,"errorMessage":"mount process hook %q: %w","messagePattern":"mount process hook %q: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/agent/hook_mount.go","lineNumber":184,"sourceCode":"\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 {\n\t\treturn nil\n\t}\n\n\tnames := make([]string, 0, len(specs))\n\tfor name, spec := range specs {\n\t\tif spec.Enabled {\n\t\t\tnames = append(names, name)\n\t\t}\n\t}","sourceCodeStart":166,"sourceCodeEnd":202,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/pkg/agent/hook_mount.go#L166-L202","documentation":"Identical failure surface to the builtin-hook mount error, but for process hooks: after NewProcessHook succeeds, AgentLoop.MountHook -> HookManager.Mount failed (nil hook manager, empty name, or nil hook). On this path the just-started subprocess is Closed before returning, so no orphan process leaks. Realistic cause is an AgentLoop built without a HookManager.","triggerScenarios":"hooks.processes.<name> starts fine but MountHook fails because al.hooks is nil — custom/hand-built AgentLoop (tests, forks) with hooks.enabled=true and a process hook enabled in config.","commonSituations":"Test harnesses constructing AgentLoop structs directly; forks with partial construction; hook manager deliberately omitted while hooks remain enabled in config.","solutions":["Build the AgentLoop with the standard constructor so a HookManager exists","Set hooks.enabled=false in configs used with hook-less loops","In tests, use the full constructor or trim the config's hooks section","Check the wrapped message ('hook manager is not initialized') to confirm wiring is the problem"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"if err := al.MountHook(reg); err != nil {\n    if strings.Contains(err.Error(), \"hook manager is not initialized\") {\n        // process already Closed by the caller; rebuild loop wiring, do not retry\n        return fmt.Errorf(\"loop constructed without hook manager: %w\", err)\n    }\n    return err\n}","preventionTips":["Use the standard AgentLoop constructor whenever hooks are enabled","Keep test configs hook-free when the test loop lacks a HookManager","Note the caller closes the subprocess on mount failure — no leak to clean up, just fix wiring and restart"],"tags":["go","picoclaw","hooks","process-hooks","wiring"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}