{"record":{"id":"127832a61598bbdf","repo":"mastra-ai/mastra","slug":"session-has-no-active-thread","errorCode":null,"errorMessage":"session has no active thread","messagePattern":"session has no active thread","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"error","filePath":"packages/server/src/server/handlers/agent-controller.ts","lineNumber":1503,"sourceCode":"    'Sets a new objective for the session\\u2019s thread. The agent\\u2019s in-loop goal judge evaluates progress after each turn.',\n  tags: ['AgentController', 'Goals'],\n  requiresAuth: true,\n  requiresPermission: 'agent-controller:execute',\n  handler: async ({\n    mastra,\n    controllerId,\n    resourceId,\n    sessionScope,\n    objective,\n    judgeModelId,\n    maxRuns,\n    requestContext,\n  }) => {\n    try {\n      const controller = getAgentControllerOrThrow(mastra, controllerId);\n      const session = await getSession(controller, resourceId, { scope: sessionScope }, requestContext);\n      const threadId = session.thread.getId();\n      if (!threadId) throw new HTTPException(400, { message: 'session has no active thread' });\n      const agent = getAgentForSession(controller, session);\n      const record = await agent.setObjective(objective, {\n        threadId,\n        resourceId: session.identity.getResourceId(),\n        ...(judgeModelId ? { judgeModelId } : {}),\n        ...(maxRuns != null ? { maxRuns } : {}),\n      });\n      return { goal: record ?? undefined };\n    } catch (error) {\n      return handleError(error, 'error setting controller goal');\n    }\n  },\n});\n\nexport const UPDATE_AGENT_CONTROLLER_GOAL_ROUTE = createRoute({\n  method: 'PUT',\n  path: '/agent-controller/:controllerId/sessions/:resourceId/goal',\n  responseType: 'json' as const,","sourceCodeStart":1485,"sourceCodeEnd":1521,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/packages/server/src/server/handlers/agent-controller.ts#L1485-L1521","documentation":"The set-objective handler resolves the controller and session, then reads the session's active thread id via `session.thread.getId()`. If the session has no active thread (null/undefined), it throws HTTP 400 because an objective cannot be attached without a thread.","triggerScenarios":"Calling the set-objective endpoint against a session that was created without a thread or whose thread was never initialized/lazy-created; session constructed lazily so no thread exists until a first message is sent.","commonSituations":"Clients setting an objective immediately after creating a session before any message/thread materialization; thread deleted server-side while the session record persists; sessions in environments where thread creation is disabled or failed silently.","solutions":["Send at least one message (or explicitly create/activate a thread) before setting an objective.","Use the session/thread creation endpoint to materialize a thread, then retry the objective call.","Verify the controller's session factory is configured to create threads on session creation.","Check server logs for earlier thread-creation failures (storage misconfig) that left the session threadless."],"exampleFix":"// before\nawait api.setObjective({ controllerId, resourceId, objective }); // 400: no thread\n// after\nawait api.sendMessage({ controllerId, resourceId, messages: [{ role: 'user', content: 'hi' }] });\nawait api.setObjective({ controllerId, resourceId, objective });","handlingStrategy":"try-catch","validationCode":"const session = await api.getSession({ controllerId, resourceId });\nif (!session.threadId) {\n  await api.sendMessage({ controllerId, resourceId, messages: [{ role: 'user', content: 'start' }] });\n}","typeGuard":"function hasActiveThread(s: { threadId?: string | null }): s is { threadId: string } {\n  return typeof s.threadId === 'string' && s.threadId.length > 0;\n}","tryCatchPattern":"try {\n  await api.setObjective({ controllerId, resourceId, objective });\n} catch (e) {\n  if (isHttpError(e) && e.status === 400 && e.message.includes('no active thread')) {\n    await api.sendMessage({ controllerId, resourceId, messages: [{ role: 'user', content: 'init' }] });\n    await api.setObjective({ controllerId, resourceId, objective });\n  } else throw e;\n}","preventionTips":["Always send an initial message before objective operations.","Surface threadId in your session UI state and gate objective actions on it.","Monitor storage health — silent thread-creation failures cause this."],"tags":["http-400","session","thread","objective"],"backgroundTag":"no-active-thread","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}