{"record":{"id":"b629d13c4fa4c557","repo":"mastra-ai/mastra","slug":"no-thread-id-available-start-a-session-before-bui","errorCode":null,"errorMessage":"No thread ID available. Start a session before building eval context.","messagePattern":"No thread ID available\\. Start a session before building eval context\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"mastracode/sdk/src/evals/context-builder.ts","lineNumber":53,"sourceCode":"  session: Session<any>;\n  /** Thread ID to build context for (defaults to current thread) */\n  threadId?: string;\n  /** Limit messages to the last N turns (user+assistant pairs). Undefined = all messages */\n  lastNTurns?: number;\n};\n\n/**\n * Build an evaluation context from a AgentController session.\n *\n * This extracts messages from storage, builds trajectory from trace spans,\n * and packages everything into the format scorers expect.\n */\nexport async function buildEvalContext(options: BuildContextOptions): Promise<MastraCodeEvalContext> {\n  const { controller, session, lastNTurns } = options;\n  const threadId = options.threadId ?? session.thread.getId();\n\n  if (!threadId) {\n    throw new Error('No thread ID available. Start a session before building eval context.');\n  }\n\n  const mastra = controller.getMastra();\n  const storage = mastra?.getStorage();\n\n  // 1. Get raw MastraDB messages from memory storage\n  const rawMessages = await getRawMessages(storage, threadId, lastNTurns);\n\n  // 2. Split messages into input/output categories\n  const { inputMessages, systemMessages, outputMessages } = categorizeMessages(rawMessages);\n\n  // 3. Extract trajectory from observability traces\n  const { trajectory, traceId } = await extractSessionTrajectory(storage, threadId);\n\n  // 4. Build request context from AgentController state\n  const requestContext = buildRequestContext(session, threadId);\n\n  return {","sourceCodeStart":35,"sourceCodeEnd":71,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/mastracode/sdk/src/evals/context-builder.ts#L35-L71","documentation":"buildEvalContext() needs a conversation thread ID to load messages from Mastra storage and build eval context. It resolves the thread ID from options.threadId or session.thread.getId(); if both yield nothing, no storage lookup is possible and it throws. Evals are meaningless without a persisted thread, so this is a fail-fast guard.","triggerScenarios":"Calling buildEvalContext({ controller, session }) where session.thread.getId() returns undefined/null and no options.threadId was supplied — typically invoking evals before the session has started a thread.","commonSituations":"Running evals in a script immediately after constructing a controller without calling a session-start API; passing a stale/detached session object whose thread was never persisted; wiring evals into a pipeline that only runs after an aborted turn.","solutions":["Start a session (so a thread is created and persisted) before calling buildEvalContext.","Pass an explicit threadId: buildEvalContext({ controller, session, threadId: existingThreadId }).","Fetch a valid thread ID from storage/memory beforehand and assert it is truthy before invoking."],"exampleFix":"// before\nawait buildEvalContext({ controller, session });\n// after\nconst threadId = session.thread.getId?.();\nif (!threadId) throw new Error('start a session first');\nawait buildEvalContext({ controller, session, threadId });","handlingStrategy":"validation","validationCode":"const threadId = options.threadId ?? session.thread?.getId?.();\nif (!threadId) {\n  throw new Error('Start a session to obtain a thread ID before building eval context.');\n}","typeGuard":"function hasThreadId(s: { thread?: { getId?: () => string | undefined } }): s is { thread: { getId: () => string } } {\n  return typeof s.thread?.getId?.() === 'string' && s.thread.getId().length > 0;\n}","tryCatchPattern":"try {\n  ctx = await buildEvalContext({ controller, session });\n} catch (err) {\n  if (err instanceof Error && err.message.includes('No thread ID available')) {\n    await session.start(); // create the thread, then retry once\n    ctx = await buildEvalContext({ controller, session });\n  } else { throw err; }\n}","preventionTips":["Always start a session (or pass an explicit threadId) before running evals.","Assert thread existence in eval pipeline setup, not inside individual evals.","Keep references to live sessions; stale/detached session objects may have no thread."],"tags":["evals","session","precondition"],"backgroundTag":"missing-thread-id","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}