{"record":{"id":"ae3533faebac3792","repo":"chenhg5/cc-connect","slug":"read-stdout-w-ae3533","errorCode":null,"errorMessage":"read stdout: %w","messagePattern":"read stdout: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"agent/cursor/session.go","lineNumber":221,"sourceCode":"\t\tline := scanner.Text()\n\t\tif line == \"\" {\n\t\t\tcontinue\n\t\t}\n\n\t\tslog.Debug(\"cursorSession: raw\", \"line\", truncateStr(line, 500))\n\n\t\tvar raw map[string]any\n\t\tif err := json.Unmarshal([]byte(line), &raw); err != nil {\n\t\t\tslog.Debug(\"cursorSession: non-JSON line\", \"line\", line)\n\t\t\tcontinue\n\t\t}\n\n\t\tcs.handleEvent(raw)\n\t}\n\n\tif err := scanner.Err(); err != nil {\n\t\tslog.Error(\"cursorSession: scanner error\", \"error\", err)\n\t\tevt := core.Event{Type: core.EventError, Error: fmt.Errorf(\"read stdout: %w\", err)}\n\t\tselect {\n\t\tcase cs.events <- evt:\n\t\tcase <-cs.ctx.Done():\n\t\t\treturn\n\t\t}\n\t}\n}\n\nfunc (cs *cursorSession) handleEvent(raw map[string]any) {\n\teventType, _ := raw[\"type\"].(string)\n\n\tswitch eventType {\n\tcase \"system\":\n\t\tcs.handleSystem(raw)\n\n\tcase \"user\":\n\t\t// User echo — nothing to do\n","sourceCodeStart":203,"sourceCodeEnd":239,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/agent/cursor/session.go#L203-L239","documentation":"The cursor agent session's stdout reader loop hits a read error while scanning the CLI process's stdout. It is wrapped with `read stdout: %w` and delivered as a core.EventError on the session's events channel, so consumers see it as a session event rather than a Send() return error. This signals the pipe to the cursor process broke or the OS read failed mid-stream.","triggerScenarios":"Calling Send() on a cursor session starts the CLI process and readLoop scans stdout; the scanner returns a non-EOF error (e.g. the cursor process crashed and closed the pipe, the OS returned EIO, or the process was killed) and readLoop emits this event.","commonSituations":"Cursor CLI crashing mid-response; user killing the process; OOM killer terminating the child; disk/pipe I/O errors; sending a prompt that makes the CLI abort abnormally.","solutions":["Read the wrapped inner error and the session's stderr/log output to find why the cursor process died","Re-create the session (start a new one) since the stdout stream is unrecoverable after a scanner error","Check the cursor CLI version and upgrade if the crash is a known bug","Verify system resources (memory, disk, fd limits) if the child process is being killed"],"exampleFix":"// before: caller treats Send success as guarantee of completion\nif err := sess.Send(prompt, id, nil, nil); err == nil { /* assume done */ }\n// after: also consume events for the terminal error\ngo func() {\n  for evt := range sess.Events() {\n    if evt.Type == core.EventError {\n      log.Printf(\"cursor stream failed: %v\", evt.Error)\n      // recreate session / notify user\n    }\n  }\n}()","handlingStrategy":"try-catch","validationCode":"// ensure the cursor CLI is healthy before opening a session\nif _, err := exec.LookPath(\"cursor-agent\"); err != nil { return fmt.Errorf(\"cursor CLI unavailable: %w\", err) }","typeGuard":null,"tryCatchPattern":"for evt := range sess.Events() {\n  if evt.Type == core.EventError {\n    var inner error\n    if errors.Unwrap(evt.Error) != nil { inner = errors.Unwrap(evt.Error) }\n    log.Printf(\"stdout stream failed: %v (cause: %v)\", evt.Error, inner)\n    // recreate session, notify user\n    break\n  }\n}","preventionTips":["Keep the cursor CLI updated","Monitor child process exits and fd/memory limits on the host","Always consume the Events channel rather than assuming Send success means completion"],"tags":["process","stdout","stream","cursor-agent"],"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"}