CherryHQ/cherry-studio · error · Error

Agent session ${session.id} became invalid while starting ta

Error message

Agent session ${session.id} became invalid while starting task

What it means

Thrown inside runAgentTask's session-start loop. Each iteration tries to start the session (requireIdle with expectedAgentId); if it does not start, is not aborted, and is not busy, the code creates a fresh session and retries once (rebound). If the second attempt still fails the same way, the loop gives up with this error — meaning a freshly created session could not be started either, indicating a deeper session-state or concurrency problem rather than a transient busy state.

Source

Thrown at src/main/ai/agents/runAgentTask.ts:342

    while (true) {
      const started = await startAgentSessionRun({
        sessionId: session.id,
        userParts: [{ type: 'text', text: effectivePrompt }],
        listeners: [sentinel, ...channelListeners],
        headless: true,
        requireIdle: { expectedAgentId: agentId }
      })
      if (started.mode === 'started') break
      if (runSignal.aborted) {
        completionActive = false
        const reason = runSignal.reason
        throw reason instanceof Error ? reason : new Error(String(reason ?? 'Task aborted'))
      }
      if (started.reason === 'busy') {
        completionActive = false
        return { sessionId: session.id, result: 'Skipped (session busy)' }
      }
      if (rebound) throw new Error(`Agent session ${session.id} became invalid while starting task`)
      rebound = true
      session = agentSessionService.create({ agentId, name: taskName ?? 'Scheduled task', workspace })
      topicId = buildAgentSessionTopicId(session.id)
      if (reuseBinding) {
        application.get('AgentJobsService').bindTaskSessionReuse({
          ...reuseBinding,
          sessionId: session.id,
          agentId,
          workspace
        })
      }
    }

    // Do not arm topic-level cancellation before admission. While this call waits for the
    // dispatch lock, the topic may legitimately belong to a user's live turn; aborting there
    // would kill exactly the stream that `requireIdle` is meant to stand down from.
    if (runSignal.aborted) onRunAbort()
    else runSignal.addEventListener('abort', onRunAbort, { once: true })

View on GitHub (pinned to 726446b54c)

Solutions

  1. Check logs for the start-loop reason (requireIdle mismatch, agentId mismatch) emitted just before the throw.
  2. Reduce concurrent dispatch against the same agent/session; serialize task starts per session.
  3. Investigate leaked completion locks (completionActive not cleared) that leave the session non-idle.
  4. Verify the reuseBinding/session-agentId wiring matches the task's agentId before retrying.
  5. As a last resort, invalidate the problem session so a clean one is created on the next run.

Example fix

// before: concurrent starts leave a fresh session non-idle
await Promise.all([start(session), start(session)])

// after: serialize starts per session; clear stale completion locks
const lock = perSessionLock(session.id)
await lock.run(() => start(session))
Defensive patterns

Strategy: try-catch

Validate before calling

if (!session.agentId || session.agentId !== agentId) {
  // do not attempt to start a mismatched session; create a fresh one explicitly
  session = agentSessionService.create({ agentId, name: taskName ?? 'Task', workspace })
}
await perSessionLock(session.id).run(() => startSession(session))

Type guard

function sessionMatches(s: { agentId?: string | null }, expectedAgentId: string): boolean {
  return s.agentId === expectedAgentId
}

Try / catch

try {
  return await runAgentTask(ctx)
} catch (e) {
  if (e instanceof Error && /became invalid while starting task/.test(e.message)) {
    // investigate leaked completion locks / concurrent starts; invalidate session and surface failure
    logger.error('Session start loop failed', { agentId, sessionId, error: e })
    return { sessionId: null, result: 'Failed (session invalid)' }
  } else throw e
}

Prevention

When it happens

Trigger: Two consecutive session-start attempts return a non-started, non-busy result (e.g. requireIdle mismatch — the session is not idle or is bound to a different agent), so a rebound session is created and also fails to start.

Common situations: Concurrent tasks for the same agent contending for the same session; an orphaned/leaked completion lock leaving the session non-idle; agentId mismatch between the task and the session; session state corruption; a misconfigured reuse binding (reuseBinding) pointing the session at the wrong agent.

Related errors


AI-assisted analysis of CherryHQ/cherry-studio@726446b54c (2026-08-12). Data as JSON: /api/errors/3c517baecaeef5c8. Report an issue: GitHub.