chenhg5/cc-connect · error

read stdout: %w

Error message

read stdout: %w

What it means

The kimi session's readLoop scans the CLI process's stdout with bufio.Scanner; if scanning aborts with an error (not a clean EOF), it logs 'scanner error' and emits core.Event{Type: EventError, Error: fmt.Errorf("read stdout: %w", scanErr)} on the session's Events channel. This error is asynchronous — the caller receives it as an event, not as a Send() return value.

Source

Thrown at agent/kimi/session.go:296

	// never sees EventError after EventResult from the same turn.
	waitErr := cmd.Wait()

	// Kimi writes "To resume this session: kimi -r <uuid>" to stderr (not stdout),
	// so the scanner above never sees it. Extract it from the captured stderr
	// buffer before emitting EventResult so the next turn can pass --resume.
	for _, line := range strings.Split(stderrBuf.String(), "\n") {
		if strings.HasPrefix(strings.TrimSpace(line), "To resume this session:") {
			if id := extractResumeSessionID(line); id != "" {
				ks.sessionID.Store(id)
				slog.Debug("kimiSession: session id from stderr", "session_id", id)
			}
			break
		}
	}

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

	if waitErr != nil {
		stderrMsg := strings.TrimSpace(stderrBuf.String())
		if stderrMsg != "" {
			slog.Error("kimiSession: process failed", "error", waitErr, "stderr", stderrMsg)
			evt := core.Event{Type: core.EventError, Error: fmt.Errorf("%s", stderrMsg)}
			select {
			case ks.events <- evt:
			case <-ks.ctx.Done():
				return
			}
			return

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Inspect the wrapped cause: if it is bufio.ErrTooLong, enlarge the scanner buffer in agent/kimi/session.go (scanner.Buffer).
  2. Check journalctl/dmesg for OOM kills of the kimi process and raise memory limits.
  3. Consume the session's Events() channel; on EventError 'read stdout', recreate the session and retry the turn.
  4. Reproduce manually: `kimi --prompt "..."` in the session workDir to see why stdout aborts.

Example fix

// before (agent/kimi/session.go readLoop)
scanner := bufio.NewScanner(stdout)

// after
scanner := bufio.NewScanner(stdout)
scanner.Buffer(make([]byte, 0, 64*1024), 10*1024*1024) // allow 10MB lines
Defensive patterns

Strategy: retry

Validate before calling

// no pre-call validation possible for async stream errors; watch events instead
go func() {
    for evt := range sess.Events() {
        if evt.Type == core.EventError {
            slog.Warn("kimi stream error", "err", evt.Error)
        }
    }
}()

Type guard

func isReadStdoutError(err error) bool {
    return err != nil && strings.Contains(err.Error(), "read stdout:")
}

Try / catch

for evt := range sess.Events() {
    if evt.Type == core.EventError && isReadStdoutError(evt.Error) {
        // recreate session and replay the last prompt
        sess, _ = agent.StartSession(ctx, opts)
        _ = sess.Send(lastPrompt, msgID, nil, nil)
    }
}

Prevention

When it happens

Trigger: The kimi process dies or its stdout pipe breaks mid-stream (crash, kill, context cancellation closing the pipe); stdout emits a single line exceeding bufio.Scanner's default 64KB token limit (bufio.ErrTooLong); an OS-level I/O error reading the pipe.

Common situations: kimi CLI crashing while printing one very large JSON line (big diffs easily exceed 64KB — hits the default Scanner limit); the CLI being OOM-killed mid-response; container shutdown terminating the process.

Related errors


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