chenhg5/cc-connect · warning

iflow session is busy

Error message

iflow session is busy

What it means

iflowSession.Send returns this when a turn is already active: `turnActive.CompareAndSwap(false, true)` failed because a previous prompt is still being processed. iflow sessions process one turn at a time, so concurrent Send calls are rejected.

Source

Thrown at agent/iflow/session.go:133

		s.sentOnce.Store(true)
	}

	return s, nil
}

func (s *iflowSession) Send(prompt string, messageID string, images []core.ImageAttachment, files []core.FileAttachment) error {
	if len(images) > 0 {
		slog.Warn("iflowSession: images are not supported, ignoring")
	}
	if len(files) > 0 {
		filePaths := core.SaveFilesToDisk(s.workDir, messageID, files)
		prompt = core.AppendFileRefs(prompt, filePaths)
	}
	if !s.alive.Load() {
		return fmt.Errorf("session is closed")
	}
	if !s.turnActive.CompareAndSwap(false, true) {
		return fmt.Errorf("iflow session is busy")
	}

	turnCtx, turnCancel := context.WithCancel(s.ctx)
	turn := &iflowTurn{
		cancel:         turnCancel,
		startedAt:      time.Now(),
		mode:           s.mode,
		pendingTimeout: s.pendingToolTimeout(),
		processDone:    make(chan struct{}),
		pendingToolIDs: make(map[string]struct{}),
		pendingTools:   make(map[string]iflowToolUse),
		seenToolIDs:    make(map[string]struct{}),
		doneToolIDs:    make(map[string]struct{}),
	}

	defer func() {
		if !s.turnActive.Load() {
			turnCancel()

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Wait for the current turn to finish before sending again; queue messages at the engine level
  2. Check busy state before calling Send (if the session exposes one) and surface a 'busy, please wait' message to the user
  3. Cancel the active turn if the new message should preempt it, then resend
  4. Use a separate session for concurrent workloads instead of sharing one

Example fix

// before
if err := session.Send(ctx, msg, nil); err != nil { return err }
// after
if err := session.Send(ctx, msg, nil); err != nil {
    if strings.Contains(err.Error(), "session is busy") {
        busyQueue <- msg // enqueue until current turn completes
        return nil
    }
    return err
}
Defensive patterns

Strategy: validation

Validate before calling

if session.IsBusy() {
    return enqueueForLater(session, msg)
}

Try / catch

if err := session.Send(ctx, msg, nil); err != nil {
    if strings.Contains(err.Error(), "session is busy") {
        return queueMessage(session, msg) // retry after turn completes
    }
    return err
}

Prevention

When it happens

Trigger: Two Send() calls on the same iflow AgentSession while the first turn is still streaming (e.g. user sends a second message before the agent finishes, or a retry timer fires mid-turn).

Common situations: User sends rapid follow-up messages in the chat platform; a queued/retry path re-sends a prompt that is already running; a cron/timer job overlaps an interactive session turn.

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/930031f1a755dba0. Report an issue: GitHub.