{"record":{"id":"620be93b14322ee7","repo":"sipeed/picoclaw","slug":"process-hook-q-s-failed-s","errorCode":null,"errorMessage":"process hook %q %s failed: %s","messagePattern":"process hook %q (.+?) failed: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/agent/hook_process.go","lineNumber":361,"sourceCode":"\t\tif err != nil {\n\t\t\tph.removePending(id)\n\t\t\treturn err\n\t\t}\n\t\tmsg.Params = body\n\t}\n\n\tif err := ph.send(ctx, msg); err != nil {\n\t\tph.removePending(id)\n\t\treturn err\n\t}\n\n\tselect {\n\tcase resp, ok := <-respCh:\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"process hook %q closed while waiting for %s\", ph.name, method)\n\t\t}\n\t\tif resp.Error != nil {\n\t\t\treturn fmt.Errorf(\"process hook %q %s failed: %s\", ph.name, method, resp.Error.Message)\n\t\t}\n\t\tif out != nil && len(resp.Result) > 0 {\n\t\t\tif err := json.Unmarshal(resp.Result, out); err != nil {\n\t\t\t\treturn fmt.Errorf(\"decode process hook %q %s result: %w\", ph.name, method, err)\n\t\t\t}\n\t\t}\n\t\treturn nil\n\tcase <-ctx.Done():\n\t\tph.removePending(id)\n\t\treturn ctx.Err()\n\t}\n}\n\nfunc (ph *ProcessHook) send(ctx context.Context, msg processHookRPCMessage) error {\n\tbody, err := json.Marshal(msg)\n\tif err != nil {\n\t\treturn err\n\t}","sourceCodeStart":343,"sourceCodeEnd":379,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/pkg/agent/hook_process.go#L343-L379","documentation":"Returned by ProcessHook.call when the hook subprocess answered the JSON-RPC request with an error object (resp.Error != nil). This is a well-formed, protocol-level failure reported by the hook itself — the message after \"failed:\" is the hook's own error text for the named method.","triggerScenarios":"Any hook-side method handler returning an error: an approve_tool hook rejecting/ failing to evaluate a tool call, a before_llm hook failing to parse its params, or the hook deliberately refusing an operation. The RPC transport itself worked; the hook's logic did not.","commonSituations":"Hook's internal validation rejects input (unknown tool name, malformed request); hook's downstream dependency (policy server, secret store) unavailable; version skew where the hook receives a method or params schema it doesn't implement.","solutions":["Read the trailing hook-provided message — it names the real cause; fix the condition inside the hook implementation","Log the hook's stderr, which usually carries the stack trace or detail behind the RPC error","If caused by params the hook doesn't understand, align hook and host versions so method schemas match","For policy hooks (approve_tool), verify the rejection is intentional vs. an internal hook bug"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":"func isHookRPCError(err error) bool {\n    return err != nil && strings.Contains(err.Error(), \" failed: \")\n}","tryCatchPattern":"err := ph.CallApproveTool(ctx, payload)\nif err != nil {\n    if isHookRPCError(err) {\n        // hook-side logic error: log and apply your policy default (deny/approve)\n        log.Printf(\"approval hook rejected: %v\", err)\n        return ErrToolDenied\n    }\n    return err\n}","preventionTips":["Log the full hook error message plus the hook's stderr on every RPC failure","Pin host and hook versions together so method semantics stay aligned","Define a safe default (e.g. deny) for when a policy hook errors"],"tags":["hooks","rpc","protocol","integration","go"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}