{"record":{"id":"3c517baecaeef5c8","repo":"CherryHQ/cherry-studio","slug":"agent-session-session-id-became-invalid-while-s","errorCode":null,"errorMessage":"Agent session ${session.id} became invalid while starting task","messagePattern":"Agent session (.+?) became invalid while starting task","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/main/ai/agents/runAgentTask.ts","lineNumber":342,"sourceCode":"    while (true) {\n      const started = await startAgentSessionRun({\n        sessionId: session.id,\n        userParts: [{ type: 'text', text: effectivePrompt }],\n        listeners: [sentinel, ...channelListeners],\n        headless: true,\n        requireIdle: { expectedAgentId: agentId }\n      })\n      if (started.mode === 'started') break\n      if (runSignal.aborted) {\n        completionActive = false\n        const reason = runSignal.reason\n        throw reason instanceof Error ? reason : new Error(String(reason ?? 'Task aborted'))\n      }\n      if (started.reason === 'busy') {\n        completionActive = false\n        return { sessionId: session.id, result: 'Skipped (session busy)' }\n      }\n      if (rebound) throw new Error(`Agent session ${session.id} became invalid while starting task`)\n      rebound = true\n      session = agentSessionService.create({ agentId, name: taskName ?? 'Scheduled task', workspace })\n      topicId = buildAgentSessionTopicId(session.id)\n      if (reuseBinding) {\n        application.get('AgentJobsService').bindTaskSessionReuse({\n          ...reuseBinding,\n          sessionId: session.id,\n          agentId,\n          workspace\n        })\n      }\n    }\n\n    // Do not arm topic-level cancellation before admission. While this call waits for the\n    // dispatch lock, the topic may legitimately belong to a user's live turn; aborting there\n    // would kill exactly the stream that `requireIdle` is meant to stand down from.\n    if (runSignal.aborted) onRunAbort()\n    else runSignal.addEventListener('abort', onRunAbort, { once: true })","sourceCodeStart":324,"sourceCodeEnd":360,"githubUrl":"https://github.com/CherryHQ/cherry-studio/blob/726446b54cd69ffe51a276638672f6d95ca0768c/src/main/ai/agents/runAgentTask.ts#L324-L360","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before: concurrent starts leave a fresh session non-idle\nawait Promise.all([start(session), start(session)])\n\n// after: serialize starts per session; clear stale completion locks\nconst lock = perSessionLock(session.id)\nawait lock.run(() => start(session))","handlingStrategy":"try-catch","validationCode":"if (!session.agentId || session.agentId !== agentId) {\n  // do not attempt to start a mismatched session; create a fresh one explicitly\n  session = agentSessionService.create({ agentId, name: taskName ?? 'Task', workspace })\n}\nawait perSessionLock(session.id).run(() => startSession(session))","typeGuard":"function sessionMatches(s: { agentId?: string | null }, expectedAgentId: string): boolean {\n  return s.agentId === expectedAgentId\n}","tryCatchPattern":"try {\n  return await runAgentTask(ctx)\n} catch (e) {\n  if (e instanceof Error && /became invalid while starting task/.test(e.message)) {\n    // investigate leaked completion locks / concurrent starts; invalidate session and surface failure\n    logger.error('Session start loop failed', { agentId, sessionId, error: e })\n    return { sessionId: null, result: 'Failed (session invalid)' }\n  } else throw e\n}","preventionTips":["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."],"tags":["agents","sessions","concurrency","jobs","state"],"backgroundTag":null,"analyzedSha":"726446b54cd69ffe51a276638672f6d95ca0768c","analyzedAt":"2026-08-12T17:30:37.448Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}