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
- Check logs for the start-loop reason (requireIdle mismatch, agentId mismatch) emitted just before the throw.
- Reduce concurrent dispatch against the same agent/session; serialize task starts per session.
- Investigate leaked completion locks (completionActive not cleared) that leave the session non-idle.
- Verify the reuseBinding/session-agentId wiring matches the task's agentId before retrying.
- 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
- Serialize session starts per session to avoid concurrent contention.
- Clear completion locks (completionActive) on every exit path.
- Keep reuse binding agentId consistent with the task agentId.
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
- Agent not found: ${agentId}
- Cannot stream on orphan session ${session.id} — its agent wa
- Agent data file must be a real file: ${filePath}
- Agent data directory already exists: ${agentDataPath}
- Agent data directory must be a real directory: ${agentDataPa
AI-assisted analysis of CherryHQ/cherry-studio@726446b54c (2026-08-12).
Data as JSON: /api/errors/3c517baecaeef5c8.
Report an issue: GitHub.