{"record":{"id":"3aa64dc40fc52fbc","repo":"CherryHQ/cherry-studio","slug":"agent-not-found-agentid-3aa64d","errorCode":null,"errorMessage":"Agent not found: ${agentId}","messagePattern":"Agent not found: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/main/ai/agents/runAgentTask.ts","lineNumber":155,"sourceCode":"      workspace\n    })\n  }\n  return session\n}\n\nexport async function runAgentTask(ctx: JobContext<AgentTaskInput>): Promise<AgentTaskOutput> {\n  const { agentId, prompt, timeoutMinutes, workspace } = ctx.input\n\n  // schedule-fired jobs carry `scheduleId` on the row; manual ad-hoc enqueues\n  // (no schedule) degrade gracefully: skip channel notification.\n  const jobSnapshot = jobService.getById(ctx.jobId)\n  const scheduleId = jobSnapshot?.scheduleId ?? null\n  const scheduleSnapshot = scheduleId ? jobScheduleService.getById(scheduleId) : null\n  const taskName = scheduleSnapshot?.name ?? null\n\n  const agent = agentService.getAgent(agentId)\n  if (!agent) {\n    throw new Error(`Agent not found: ${agentId}`)\n  }\n\n  const config = agent.configuration ?? {}\n\n  const isHeartbeat = taskName === HEARTBEAT_TASK_NAME && prompt === HEARTBEAT_PROMPT_SENTINEL\n\n  let effectivePrompt = prompt\n\n  if (isHeartbeat) {\n    if (config.heartbeat_enabled === false) {\n      logger.debug('Heartbeat skipped (disabled)', { agentId, scheduleId })\n      return { sessionId: null, result: 'Skipped (disabled)' }\n    }\n    switch (workspace.type) {\n      case AGENT_WORKSPACE_TYPE.SYSTEM:\n        logger.debug('Heartbeat skipped (no file)', { agentId, scheduleId })\n        return { sessionId: null, result: 'Skipped (no file)' }\n      case AGENT_WORKSPACE_TYPE.USER:","sourceCodeStart":137,"sourceCodeEnd":173,"githubUrl":"https://github.com/CherryHQ/cherry-studio/blob/726446b54cd69ffe51a276638672f6d95ca0768c/src/main/ai/agents/runAgentTask.ts#L137-L173","documentation":"Thrown by runAgentTask when agentService.getAgent(agentId) returns null/falsy before dispatching the job. The job (schedule-fired or ad-hoc) references an agentId that no longer resolves to a stored agent — typically because the agent was deleted after the job was enqueued/scheduled but before it ran.","triggerScenarios":"A scheduled or manually-enqueued agent task executes (runAgentTask) for an agentId that agentService.getAgent cannot find.","commonSituations":"The user deleted the agent while its heartbeat/ad-hoc schedule was still active; a schedule was imported pointing at a non-existent agent; agentId mismatch between the job row and the agents table; the agent failed to provision/create.","solutions":["Verify the agentId exists: `agentService.getAgent(agentId)` — if it is gone, the schedule is stale.","When an agent is deleted, also delete/disable its job schedules and cancel in-flight jobs (see jobScheduleService / jobService).","If the agent should exist, check why getAgent returns null (provisioning failure, DB row missing).","Catch the error at the job layer and mark the schedule inactive so it does not keep firing."],"exampleFix":"// before: agent deleted but schedule still fires\nconst agent = agentService.getAgent(agentId)\nif (!agent) throw new Error(`Agent not found: ${agentId}`)\n\n// after: on agent delete, tear down its schedules\nawait jobScheduleService.cancelSchedulesForAgent(agentId)\nawait jobService.cancelJobsForAgent(agentId)\nawait agentService.delete(agentId)","handlingStrategy":"type-guard","validationCode":"const agent = agentService.getAgent(agentId)\nif (!agent) {\n  // cancel stale schedule/job instead of letting runAgentTask throw\n  await jobScheduleService.cancelSchedulesForAgent(agentId).catch(() => {})\n  await jobService.cancelJobsForAgent(agentId).catch(() => {})\n  return\n}","typeGuard":"function agentExists(a: unknown): a is { id: string; configuration: Record<string, unknown> } {\n  return a != null && typeof (a as { id?: unknown }).id === 'string'\n}","tryCatchPattern":"try {\n  return await runAgentTask(ctx)\n} catch (e) {\n  if (e instanceof Error && /Agent not found:/.test(e.message)) {\n    // mark the owning schedule inactive so it stops firing\n    await jobScheduleService.deactivate(scheduleId).catch(() => {})\n    return { sessionId: null, result: 'Skipped (agent deleted)' }\n  } else throw e\n}","preventionTips":["On agent deletion, cancel its schedules and in-flight jobs.","Validate agentId existence before enqueueing tasks.","Mark schedules inactive when their agent is missing."],"tags":["agents","jobs","schedules","lifecycle","not-found"],"backgroundTag":null,"analyzedSha":"726446b54cd69ffe51a276638672f6d95ca0768c","analyzedAt":"2026-08-12T17:30:37.448Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}