{"record":{"id":"d1f5567efcea411a","repo":"chenhg5/cc-connect","slug":"hook-input-is-not-valid-json","errorCode":null,"errorMessage":"hook input is not valid JSON","messagePattern":"hook input is not valid JSON","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"agent/antigravityhook/protocol.go","lineNumber":45,"sourceCode":"\tDecision string `json:\"decision\"`\n\tReason   string `json:\"reason,omitempty\"`\n}\n\n// Relay forwards one Agy hook invocation to the owning cc-connect session.\nfunc Relay(in io.Reader, out io.Writer, address, token string) error {\n\tif strings.TrimSpace(address) == \"\" || strings.TrimSpace(token) == \"\" {\n\t\treturn fmt.Errorf(\"permission bridge environment is missing\")\n\t}\n\n\tinput, err := io.ReadAll(io.LimitReader(in, maxHookInput+1))\n\tif err != nil {\n\t\treturn fmt.Errorf(\"read hook input: %w\", err)\n\t}\n\tif len(input) > maxHookInput {\n\t\treturn fmt.Errorf(\"hook input exceeds %d bytes\", maxHookInput)\n\t}\n\tif !json.Valid(input) {\n\t\treturn fmt.Errorf(\"hook input is not valid JSON\")\n\t}\n\n\tconn, err := net.DialTimeout(\"tcp\", address, bridgeDialTimeout)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"connect permission bridge: %w\", err)\n\t}\n\tdefer func() { _ = conn.Close() }()\n\t// The listener is started before agy runs this hook, so dial failures should\n\t// fail closed quickly. After connect, wait much longer for a human response.\n\t_ = conn.SetDeadline(time.Now().Add(bridgeResponseTimeout))\n\n\tif err := json.NewEncoder(conn).Encode(BridgeRequest{Token: token, HookInput: input}); err != nil {\n\t\treturn fmt.Errorf(\"send permission request: %w\", err)\n\t}\n\n\tvar response BridgeResponse\n\tif err := json.NewDecoder(io.LimitReader(conn, 64<<10)).Decode(&response); err != nil {\n\t\treturn fmt.Errorf(\"read permission response: %w\", err)","sourceCodeStart":27,"sourceCodeEnd":63,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/agent/antigravityhook/protocol.go#L27-L63","documentation":"Relay validates that the hook's stdin payload is well-formed JSON using json.Valid and returns 'hook input is not valid JSON' otherwise. The payload is forwarded verbatim to the bridge, so it must be valid JSON before it is sent.","triggerScenarios":"The bytes read from stdin fail json.Valid — e.g. empty input, truncated payload, binary data, or manually piping non-JSON into the hook.","commonSituations":"Debugging the hook by running it manually without piping valid JSON; agy producing truncated output (possibly overlapping with the size cap); corrupted stdin wiring in custom wrappers.","solutions":["Dump the exact stdin bytes (`tee /tmp/hookin.json`) to inspect what agy sent","Test with a known-good minimal payload: `echo '{\"session_id\":\"x\"}' | hook`","Ensure wrapper scripts don't alter or truncate stdin","Check whether the payload was cut off at maxHookInput, producing truncated JSON"],"exampleFix":"// before (manual test)\n$ agy-permission-hook\n// after\n$ echo '{\"tool\":\"bash\",\"command\":\"ls\"}' | agy-permission-hook","handlingStrategy":"validation","validationCode":"raw, _ := io.ReadAll(os.Stdin)\nif !json.Valid(raw) {\n    os.Stderr.WriteString(\"hook stdin must be valid JSON from agy\\n\")\n    os.Exit(2)\n}","typeGuard":null,"tryCatchPattern":"if err := Relay(...); err != nil && strings.Contains(err.Error(), \"not valid JSON\") {\n    fmt.Fprintln(os.Stderr, \"invalid hook input; verify agy invocation\")\n    os.Exit(2)\n}","preventionTips":["Never pipe hand-typed text into the hook; always valid JSON","Check for truncation if inputs approach the size cap","Keep hook wrapper scripts pass-through (no stdin rewriting)"],"tags":["json","validation","hook"],"backgroundTag":"schema-validation-failed","analyzedSha":"4000b2338aa6e850c99df54f8b0ed6ed7460b401","analyzedAt":"2026-09-06T11:45:09.575Z","contentChangedAt":"2026-09-06T11:45:09.575Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}