{"record":{"id":"28ab2eecf4fd42ce","repo":"chenhg5/cc-connect","slug":"read-stdout-w-28ab2e","errorCode":null,"errorMessage":"read stdout: %w","messagePattern":"read stdout: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"agent/claudecode/session.go","lineNumber":587,"sourceCode":"\tclose(cs.events)\n\tclose(cs.done)\n}\n\nfunc (cs *claudeSession) handleReadLoopScanErr(err error, waitDone <-chan struct{}) {\n\tif err == nil {\n\t\treturn\n\t}\n\n\tselect {\n\tcase <-cs.ctx.Done():\n\t\treturn\n\tcase <-waitDone:\n\t\treturn\n\tdefault:\n\t}\n\n\tslog.Error(\"claudeSession: scanner error\", \"error\", err)\n\tevt := core.Event{Type: core.EventError, Error: fmt.Errorf(\"read stdout: %w\", err)}\n\tselect {\n\tcase cs.events <- evt:\n\tcase <-cs.ctx.Done():\n\t\treturn\n\t}\n}\n\nfunc (cs *claudeSession) handleReadLoopLine(line string) {\n\tif line == \"\" {\n\t\treturn\n\t}\n\n\tvar raw map[string]any\n\tif err := json.Unmarshal([]byte(line), &raw); err != nil {\n\t\tslog.Debug(\"claudeSession: non-JSON line\", \"line\", line)\n\t\treturn\n\t}\n","sourceCodeStart":569,"sourceCodeEnd":605,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/agent/claudecode/session.go#L569-L605","documentation":"handleReadLoopScanErr fires when the bufio.Scanner reading the claude process's stdout returns a non-EOF error. The raw scanner error is wrapped as 'read stdout: %w' and emitted as a core.EventError. This means the stdout stream broke unexpectedly mid-session (process died, pipe closed, or — commonly — bufio.Scanner's 64KB default token limit exceeded).","triggerScenarios":"readLoop's scanner hits Scan() error: the claude process died and the pipe broke, the OS returned an I/O error, or a single stdout line exceeded bufio.MaxScanTokenSize (64KB) producing bufio.ErrTooLong.","commonSituations":"Very large assistant messages / huge tool outputs from Claude Code producing one JSON line over 64KB; abrupt CLI crash mid-stream; session killed while a line was being read.","solutions":["If the wrapped error is bufio.ErrTooLong, enlarge the scanner buffer with scanner.Buffer(make([]byte, 0, 64*1024), 10*1024*1024) in readLoop before scanning","Check whether the claude process is still alive (`ps -p <pid>`); if dead, treat as a session crash and recreate via StartSession","Treat the emitted EventError as terminal: close/reopen the session rather than retrying Send on the dead pipe","If it reproduces with huge outputs, reduce max output size in agent settings or split requests"],"exampleFix":"// before\nscanner := bufio.NewScanner(stdout)\n// after\nscanner := bufio.NewScanner(stdout)\nscanner.Buffer(make([]byte, 0, 64*1024), 10*1024*1024)","handlingStrategy":"fallback","validationCode":"// no pre-call validation possible; mitigate by raising the scanner limit in readLoop\nscanner := bufio.NewScanner(stdout)\nscanner.Buffer(make([]byte, 0, 64*1024), 10*1024*1024) // allow 10MB lines","typeGuard":null,"tryCatchPattern":"for evt := range sess.Events() {\n    if evt.Type == core.EventError && strings.Contains(evt.Error.Error(), \"read stdout\") {\n        if errors.Is(evt.Error, bufio.ErrTooLong) {\n            // oversized line: upgrade scanner buffer / limit output size\n        }\n        sess = recreateSession() // stream is broken; session unusable\n    }\n}","preventionTips":["Always enlarge bufio.Scanner buffers when parsing LLM JSON-lines output","Monitor for CLI crashes and auto-recreate sessions on EventError","Limit per-response output size in agent settings to keep lines under buffer limits","Check disk/pipe health if I/O errors repeat on the same host"],"tags":["go","bufio","pipe","streaming"],"backgroundTag":"broken-pipe","analyzedSha":"4000b2338aa6e850c99df54f8b0ed6ed7460b401","analyzedAt":"2026-09-06T11:45:09.575Z","contentChangedAt":"2026-09-06T11:45:09.575Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}