chenhg5/cc-connect · error

session is closed

Error message

session is closed

What it means

agent/pi/session.go:327 — piSession.Send checks s.alive before doing any work; if the session has already been closed or the RPC process has died, it returns the plain error "session is closed". This guards against writing to a dead process's stdin after Close() or after the read loop observed process exit.

Source

Thrown at agent/pi/session.go:327

		select {
		case s.events <- evt:
		case <-s.ctx.Done():
		}
	}
	s.alive.Store(false)
}

// ── Send ─────────────────────────────────────────────────────

// Send writes a prompt to the Pi agent.
// In json mode (default): spawns a one-shot `pi --mode json` process.
// In rpc mode: writes a "prompt" command to the persistent RPC process stdin.
func (s *piSession) Send(msg string, messageID string, images []core.ImageAttachment, files []core.FileAttachment) error {
	s.sendWg.Add(1)
	defer s.sendWg.Done()

	if !s.alive.Load() {
		return fmt.Errorf("session is closed")
	}

	attachDir := s.attachDir
	if safeMessageID := sanitizePiAttachmentName(messageID); safeMessageID != "" {
		attachDir = filepath.Join(attachDir, safeMessageID)
	}
	cleanAttachments(attachDir)

	// Issue #1723: images are passed via pi's @<path> argv / message-text
	// mechanism. pi's processImage loads them as visual inputs and the
	// bytes never enter our prompt text.
	//
	// Issue #1767: non-image files are NOT passed via @<path>. pi's
	// processFileArguments reads every @<path> file's full UTF-8 contents
	// and inlines them into the prompt the model sees. For a >~1MB text
	// attachment this can blow the model's context and trigger a 400 from
	// the provider. Mirror the claudecode behaviour: save non-image files
	// to disk and tell the model where they live via a plain path

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Create a new session (e.g. /new or the platform's session-creation command) and resend the message.
  2. If using /switch, ensure the target session id exists and is alive; a closed session id cannot accept prompts.
  3. Check daemon logs for why the session died (preceding "pi: ..." stderr events) and fix that root cause.
  4. In calling code, treat this as terminal: do not retry on the same session object; recreate via the agent's StartSession.

Example fix

// before
err := session.Send(msg, id, nil, nil) // session already stopped
// after
if !sessionAlive(session) {
    session, err = agent.StartSession(ctx, sessionID, workDir)
}
err = session.Send(msg, id, nil, nil)
Defensive patterns

Strategy: validation

Validate before calling

func ensureAlive(sess *piSession) error {
    if sess == nil || !sess.alive.Load() {
        return errors.New("session is closed; create a new session before sending")
    }
    return nil
}

Try / catch

if err := session.Send(msg, id, nil, nil); err != nil {
    if strings.Contains(err.Error(), "session is closed") {
        session, err = agent.StartSession(ctx, sessionID, workDir)
        if err == nil {
            err = session.Send(msg, id, nil, nil)
        }
    }
    return err
}

Prevention

When it happens

Trigger: Calling Send (directly or via the engine routing a platform message) when: (1) Close() was called on the session; (2) readLoopRPC finished (process exited) and set alive=false; (3) Send races with a concurrent Close from /stop or session timeout; (4) a queued message is delivered after the process crashed.

Common situations: User sends a message to a chat whose agent session was stopped with /stop or timed out; engine replays a queued message after pi crashed; a long-running turn outlived the session and the next prompt arrives post-exit.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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