{"record":{"id":"514b3f6fda08b999","repo":"mastra-ai/mastra","slug":"thread-id-is-required-for-thread-scoped-working-me-514b3f","errorCode":null,"errorMessage":"Thread ID is required for thread-scoped working memory updates","messagePattern":"Thread ID is required for thread-scoped working memory updates","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/memory/src/tools/working-memory.ts","lineNumber":210,"sourceCode":"    // Strict structured outputs would force every field into `required`, so the model has to\n    // emit placeholder values for untouched sections, which then overwrite stored data.\n    ...(usesMergeSemantics ? { strict: false as const } : {}),\n    execute: async (inputData, context) => {\n      const workingMemoryInput = inputData as { memory: any };\n      const threadId = context?.agent?.threadId;\n      const resourceId = context?.agent?.resourceId;\n\n      // Memory can be accessed via context.memory (when agent is part of Mastra instance)\n      // or context.memory (when agent is standalone with memory passed directly)\n      const memory = (context as any)?.memory;\n\n      if (!memory) {\n        throw new Error('Memory instance is required for working memory updates');\n      }\n\n      const scope = memoryConfig?.workingMemory?.scope || 'resource';\n      if (scope === 'thread' && !threadId) {\n        throw new Error('Thread ID is required for thread-scoped working memory updates');\n      }\n      if (scope === 'resource' && !resourceId) {\n        throw new Error('Resource ID is required for resource-scoped working memory updates');\n      }\n\n      if (threadId) {\n        let thread = await memory.getThreadById({ threadId });\n\n        if (!thread) {\n          thread = await memory.createThread({\n            threadId,\n            resourceId,\n            memoryConfig,\n          });\n        }\n\n        if (thread.resourceId && resourceId && thread.resourceId !== resourceId) {\n          throw new Error(`Thread with id ${threadId} resourceId does not match the current resourceId ${resourceId}`);","sourceCodeStart":192,"sourceCodeEnd":228,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/packages/memory/src/tools/working-memory.ts#L192-L228","documentation":"The update-working-memory tool was configured with workingMemory.scope = 'thread', meaning memory is scoped per conversation thread, but the tool executed without a threadId in its agent context. Mastra throws this to prevent writing thread-scoped memory without knowing which thread it belongs to. It is a fail-fast guard in the tool's execute function, not a storage failure.","triggerScenarios":"Calling agent.generate/stream without providing threadId (e.g. memory options omitted) while the agent's memory is configured with workingMemory scope 'thread', so the model invokes the update-working-memory tool and context.agent.threadId is undefined.","commonSituations":"Migrating from default resource scope to thread scope without updating call sites; calling agent.generate({ messages }) in scripts/tests with no memory: { thread, resource } option; running the agent inside a workflow step that forgets to pass thread context.","solutions":["Pass threadId when calling the agent: agent.generate(input, { memory: { thread: threadId, resource: resourceId } }).","Switch workingMemory scope back to 'resource' in the Memory config if per-thread scoping is not actually needed.","Ensure threadId is propagated if the agent runs in a workflow/custom executor that builds its own tool context.","Guard the call site: only run the agent when threadId is present (throw early in your own code)."],"exampleFix":"// before\nawait agent.generate('Save that I like pizza');\n// after\nawait agent.generate('Save that I like pizza', {\n  memory: { thread: 'thread-123', resource: 'user-42' },\n});","handlingStrategy":"validation","validationCode":"if (!threadId) throw new Error('threadId is required before running a thread-scoped-memory agent');","typeGuard":"const hasThreadId = (v: unknown): v is { threadId: string } =>\n  typeof v === 'object' && v !== null && typeof (v as any).threadId === 'string' && (v as any).threadId.length > 0;","tryCatchPattern":"try {\n  await agent.generate(input, { memory: { thread, resource } });\n} catch (e) {\n  if (e instanceof Error && e.message.includes('Thread ID is required for thread-scoped')) {\n    // fall back to resource scope or re-run with a threadId\n  }\n}","preventionTips":["Always pass memory: { thread, resource } together when calling generate/stream.","Type the memory options object so threadId is required, not optional, in your app's helper wrapper.","Keep scope config and call sites in sync; document scope choice near the Memory constructor.","Add an integration test that runs the agent with thread-scoped memory."],"tags":["memory","working-memory","configuration","missing-argument"],"backgroundTag":"missing-required-identifier","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}