chenhg5/cc-connect · error
parse hook output: %w
Error message
parse hook output: %w
What it means
parseHookOutput parses the permission hook's stdout JSON into ccHookDecision. This error wraps json.Unmarshal failure on the trimmed stdout, meaning the hook printed something that is not valid JSON (or a JSON shape differing from the expected hookSpecificOutput.decision wrapper).
Source
Thrown at agent/claudecode/cc_hooks.go:299
case "allow":
return ccHookDecision{Behavior: "allow"}, nil
case "deny":
return ccHookDecision{Behavior: "deny"}, nil
case "ask":
return ccHookDecision{}, nil
}
// Try structured JSON output.
var out struct {
HookSpecificOutput struct {
Decision struct {
Behavior string `json:"behavior"`
Message string `json:"message"`
} `json:"decision"`
} `json:"hookSpecificOutput"`
}
if err := json.Unmarshal(trimmed, &out); err != nil {
return ccHookDecision{}, fmt.Errorf("parse hook output: %w", err)
}
behavior := strings.ToLower(out.HookSpecificOutput.Decision.Behavior)
if behavior == "allow" || behavior == "deny" {
return ccHookDecision{
Behavior: behavior,
Message: out.HookSpecificOutput.Decision.Message,
}, nil
}
return ccHookDecision{}, nil
}
// buildHookStdin constructs the JSON payload for the hook's stdin,
// matching Claude Code's PermissionRequest hook input spec.
func buildHookStdin(hctx hookContext) map[string]any {
m := map[string]any{
"session_id": hctx.sessionID,
"hook_event_name": "PermissionRequest",
"tool_name": hctx.toolName,View on GitHub (pinned to 4000b2338a)
Solutions
- Fix the hook script so its final stdout line is exactly one JSON object with hookSpecificOutput.decision.behavior of "allow" or "deny"
- Move any logging in the hook to stderr so stdout stays clean JSON
- Check the hook's schema against Claude Code's current permission-hook output format and update keys
- Run the hook manually and pipe stdout through `jq` to confirm it parses
Example fix
// before (hook script)
echo "checking permission..."
echo '{"behavior":"allow"}'
// after
echo "checking permission..." >&2
echo '{"hookSpecificOutput":{"decision":{"behavior":"allow","message":"ok"}}}' Defensive patterns
Strategy: validation
Validate before calling
out, _ := sh("-c", hookCommand)
trimmed := strings.TrimSpace(out)
if !json.Valid([]byte(trimmed)) { return fmt.Errorf("hook stdout is not JSON: %q", trimmed) } Type guard
func emitsHookJSON(cmd string) bool {
out, err := exec.Command("sh", "-c", cmd).Output()
if err != nil { return false }
var v struct{ HookSpecificOutput struct{ Decision struct{ Behavior string `json:"behavior"` } `json:"decision"` } `json:"hookSpecificOutput"` }
return json.Unmarshal([]byte(strings.TrimSpace(string(out))), &v) == nil
} Try / catch
decision, err := parseHookOutput(stdout.Bytes())
if err != nil {
log.Errorf("hook stdout not parseable: %v; raw=%q", err, string(stdout.Bytes()))
return ccHookDecision{}, err
} Prevention
- Hook scripts must print exactly one JSON object to stdout; send all logging to stderr
- Keep hook output schema in sync with the Claude Code permission-hook spec
- Verify with `sh -c '<hook>' | jq .` before deploying
- Avoid echo/prints of debug text in hook scripts
When it happens
Trigger: runHookCommand → parseHookOutput(stdout.Bytes()) fails when the hook command prints human-readable logs, empty output, or JSON without the expected {"hookSpecificOutput":{"decision":{"behavior":...}}} nesting, so Unmarshal errors or returns fields that don't fit.
Common situations: Hook script echoes debug text before/instead of the JSON decision; hook uses an older Claude Code hook output schema; hook prints nothing and the unmarshal fails on empty input; hook emits valid JSON but a different key layout.
Understand the failure class
Background: "failed to unmarshal" / json.Unmarshal errors: why parsing a response into a Go struct fails and how to fix it — this error's family across 23 libraries.
Related errors
- parse %s: %w
- marshal stdin: %w
- parse existing Agy hooks %s: %w
- marshal Agy permission hook: %w
- marshal Agy hooks overlay: %w
AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06).
Data as JSON: /api/errors/288489f59b11c423.
Report an issue: GitHub.