chenhg5/cc-connect · error

read stdout: %w

Error message

read stdout: %w

What it means

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.

Source

Thrown at agent/cursor/session.go:221

		line := scanner.Text()
		if line == "" {
			continue
		}

		slog.Debug("cursorSession: raw", "line", truncateStr(line, 500))

		var raw map[string]any
		if err := json.Unmarshal([]byte(line), &raw); err != nil {
			slog.Debug("cursorSession: non-JSON line", "line", line)
			continue
		}

		cs.handleEvent(raw)
	}

	if err := scanner.Err(); err != nil {
		slog.Error("cursorSession: scanner error", "error", err)
		evt := core.Event{Type: core.EventError, Error: fmt.Errorf("read stdout: %w", err)}
		select {
		case cs.events <- evt:
		case <-cs.ctx.Done():
			return
		}
	}
}

func (cs *cursorSession) handleEvent(raw map[string]any) {
	eventType, _ := raw["type"].(string)

	switch eventType {
	case "system":
		cs.handleSystem(raw)

	case "user":
		// User echo — nothing to do

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Read the wrapped inner error and the session's stderr/log output to find why the cursor process died
  2. Re-create the session (start a new one) since the stdout stream is unrecoverable after a scanner error
  3. Check the cursor CLI version and upgrade if the crash is a known bug
  4. Verify system resources (memory, disk, fd limits) if the child process is being killed

Example fix

// before: caller treats Send success as guarantee of completion
if err := sess.Send(prompt, id, nil, nil); err == nil { /* assume done */ }
// after: also consume events for the terminal error
go func() {
  for evt := range sess.Events() {
    if evt.Type == core.EventError {
      log.Printf("cursor stream failed: %v", evt.Error)
      // recreate session / notify user
    }
  }
}()
Defensive patterns

Strategy: try-catch

Validate before calling

// ensure the cursor CLI is healthy before opening a session
if _, err := exec.LookPath("cursor-agent"); err != nil { return fmt.Errorf("cursor CLI unavailable: %w", err) }

Try / catch

for evt := range sess.Events() {
  if evt.Type == core.EventError {
    var inner error
    if errors.Unwrap(evt.Error) != nil { inner = errors.Unwrap(evt.Error) }
    log.Printf("stdout stream failed: %v (cause: %v)", evt.Error, inner)
    // recreate session, notify user
    break
  }
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06). Data as JSON: /api/errors/ae3533faebac3792. Report an issue: GitHub.