chenhg5/cc-connect · error

acp: session closed

Error message

acp: session closed

What it means

Sentinel error returned by acpSession.Send when the session's alive flag is false, i.e. the agent process has exited (or the session was closed) before the prompt could be sent. Callers get this instead of attempting a JSON-RPC call on a dead transport.

Source

Thrown at agent/acp/session.go:592

			ToolInputRaw: rawTool,
			SessionID:    s.currentACPSessionID(),
		})
	}()
}

func (s *acpSession) emit(ev core.Event) {
	if ev.SessionID == "" {
		ev.SessionID = s.currentACPSessionID()
	}
	select {
	case s.events <- ev:
	case <-s.ctx.Done():
	}
}

func (s *acpSession) Send(prompt string, messageID string, images []core.ImageAttachment, files []core.FileAttachment) error {
	if !s.alive.Load() {
		return fmt.Errorf("acp: session closed")
	}

	s.sendMu.Lock()
	defer s.sendMu.Unlock()

	filePaths := core.SaveFilesToDisk(s.workDir, messageID, files)
	prompt = core.AppendFileRefs(prompt, filePaths)
	if len(images) > 0 {
		prompt = s.appendImageRefs(prompt, images)
	}

	sid := s.currentACPSessionID()
	if sid == "" {
		return fmt.Errorf("acp: no agent session id")
	}

	promptBlocks := []any{
		map[string]any{"type": "text", "text": prompt},

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Check the process-exit log line (`acp: process exited`) and its stderr to find why the agent died before retrying.
  2. Start a new session (the engine will spawn a fresh agent process) and resend the prompt.
  3. Fix the underlying crash: raise memory limits, update the agent, or remove the offending prompt/flag.
  4. Avoid sending on a stale session reference after Close/Stop; let the engine recreate the session via its normal path.

Example fix

// before: caller keeps a dead session
err := sess.Send(prompt, id, nil, nil) // "acp: session closed"
// after: recreate the session when it is no longer alive
if !sessAlive(sess) {
    sess, err = agent.StartSession(ctx, sessionID, nil)
}
err = sess.Send(prompt, id, nil, nil)
Defensive patterns

Strategy: validation

Validate before calling

func canSend(s core.AgentSession) bool {
    type aliver interface{ IsAlive() bool }
    a, ok := s.(aliver)
    return !ok || a.IsAlive()
}

Type guard

func sessionAlive(s *acpSession) bool { return s != nil && s.alive.Load() }

Try / catch

if err := sess.Send(prompt, mid, nil, nil); err != nil {
    if strings.Contains(err.Error(), "acp: session closed") {
        // recreate session and resend once
        ns, serr := agent.StartSession(ctx, core.ContinueSession, nil)
        if serr == nil { return ns.Send(prompt, mid, nil, nil) }
    }
    return err
}

Prevention

When it happens

Trigger: Calling Send after the agent process exited (readLoop observed cmd.Wait return and stored alive=false), after Close() was called, or a race where the process dies between engine calls.

Common situations: Agent crashed mid-conversation (OOM, panic, fatal stderr); user ran /stop closing the session while a queued message was being sent; system shutdown cancelled s.ctx; idle timeout killed the agent.

Related errors


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