mastra-ai/mastra · error · HTTPException

thread "${requestedThreadId}" not found

Error message

thread "${requestedThreadId}" not found

What it means

When a session endpoint is called with an explicit `requestedThreadId`, the handler loads the thread via the memory store scoped to the given resourceId and throws HTTP 404 if no matching thread exists. This ensures clients cannot attach a session to a nonexistent (or other-resource) thread.

Source

Thrown at packages/server/src/server/handlers/agent-controller.ts:820

  pathParamSchema: sessionPathParams,
  queryParamSchema: sessionStateQuerySchema,
  responseSchema: sessionStateResponseSchema,
  summary: 'Get session state',
  description: 'Returns the current mode, model, thread, and durable tasks for initial UI hydration.',
  tags: ['AgentController'],
  requiresAuth: true,
  requiresPermission: 'agent-controller:read',
  handler: async ({ mastra, controllerId, resourceId, sessionScope, threadId: requestedThreadId, requestContext }) => {
    try {
      const controller = getAgentControllerOrThrow(mastra, controllerId);
      const session = await getSession(controller, resourceId, { scope: sessionScope }, requestContext);
      const ds = session.displayState.get();
      const threadId = requestedThreadId ?? session.thread.getId() ?? undefined;
      const storage = mastra.getStorage();
      if (requestedThreadId) {
        const memory = await storage?.getStore('memory');
        const thread = await memory?.getThreadById({ threadId: requestedThreadId, resourceId });
        if (!thread) throw new HTTPException(404, { message: `thread "${requestedThreadId}" not found` });
      }
      const threadState = threadId ? await storage?.getStore('threadState') : undefined;
      const storedTasks = threadId ? await threadState?.getState<unknown>({ threadId, type: 'task' }) : undefined;
      const parsedTasks = taskSnapshotSchema.array().safeParse(storedTasks);
      const tasks: SessionTaskSnapshot[] = parsedTasks.success ? parsedTasks.data : [];
      const om = ds.omProgress;
      const reflectionSavings =
        om.buffered.reflection.inputObservationTokens - om.buffered.reflection.observationTokens;
      const st = session.state.get() as Record<string, unknown>;
      const oneOf = <T extends string>(value: unknown, allowed: readonly T[], fallback: T): T =>
        allowed.includes(value as T) ? (value as T) : fallback;
      const oneOfOptional = <T extends string>(value: unknown, allowed: readonly T[]): T | undefined =>
        allowed.includes(value as T) ? (value as T) : undefined;
      return {
        controllerId,
        resourceId,
        threadId,
        modeId: session.mode.get(),

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Verify the threadId exists and belongs to the same resourceId — drop the explicit threadId to let the session create/reuse its own thread.
  2. Create the thread first (via the session/thread creation endpoint) before referencing it.
  3. Check storage configuration: ensure the memory store backing the server is the one that contains the thread.
  4. Clear stale client-side thread id caches after restarts or storage migrations.

Example fix

// before
const s = await api.createSession({ resourceId, threadId: savedThreadId }); // 404 after storage reset
// after
const s = await api.createSession({ resourceId }); // let server create/reuse a thread
Defensive patterns

Strategy: try-catch

Validate before calling

const thread = await memoryClient.getThreadById({ threadId: requestedThreadId, resourceId });
if (!thread) throw new Error(`Thread ${requestedThreadId} does not exist for ${resourceId}; omit threadId to create one`);

Type guard

function threadExists(t: { id: string } | null | undefined, id: string): t is { id: string } {
  return !!t && t.id === id;
}

Try / catch

try {
  session = await api.createSession({ resourceId, threadId: requestedThreadId });
} catch (e) {
  if (isHttpError(e) && e.status === 404 && e.message.includes('not found')) {
    session = await api.createSession({ resourceId }); // create a fresh thread
  } else throw e;
}

Prevention

When it happens

Trigger: Passing a threadId in the request body/params that was never created, was deleted, belongs to a different resourceId (resource scoping is applied in getThreadById), or whose storage backend changed (e.g. switched storage domains and old thread ids no longer resolve).

Common situations: Client caches thread ids across environments (dev vs prod storage); in-memory storage reset on server restart so old ids vanish; resourceId mismatch because the identity/resource changed; typos in persisted thread id.

Related errors


AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30). Data as JSON: /api/errors/993d7439e12c2a31. Report an issue: GitHub.