{"record":{"id":"bd53c9ccafb059ab","repo":"mastra-ai/mastra","slug":"thread-with-id-threadid-resourceid-does-not-mat-bd53c9","errorCode":null,"errorMessage":"Thread with id ${threadId} resourceId does not match the current resourceId ${resourceId}","messagePattern":"Thread with id (.+?) resourceId does not match the current resourceId (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/memory/src/tools/working-memory.ts","lineNumber":228,"sourceCode":"        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}`);\n        }\n      }\n\n      let workingMemory: string;\n\n      if (usesMergeSemantics) {\n        // Schema-based: fetch existing, merge, save\n        const existingRaw = await memory.getWorkingMemory({\n          threadId,\n          resourceId,\n          memoryConfig,\n        });\n\n        let existingData: Record<string, unknown> | null = null;\n        if (existingRaw) {\n          try {\n            existingData = typeof existingRaw === 'string' ? JSON.parse(existingRaw) : existingRaw;\n          } catch {","sourceCodeStart":210,"sourceCodeEnd":246,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/packages/memory/src/tools/working-memory.ts#L210-L246","documentation":"The thread found by threadId is already owned by a different resourceId than the one supplied in this request. Mastra checks thread.resourceId against the incoming resourceId to stop cross-user/cross-resource writes and memory leakage between resources.","triggerScenarios":"Calling the agent with a memory options pair where the threadId belongs to user A but resourceId is user B (e.g. mixed-up session mapping, or reusing a hard-coded threadId across users).","commonSituations":"Hard-coded or cached threadIds in demos/tests reused across different resource IDs; a client passing another user's threadId; after switching auth user IDs while keeping old thread IDs; keying threads by something that changed (e.g. email -> user id).","solutions":["Make threadId unique per resource (e.g. `${resourceId}:conversationId`) and never reuse threads across resources.","Verify the resourceId sent from the client matches the thread's owner; fix the mapping in your app state.","If the thread ownership changed legitimately, create a new thread for the new resource instead of reusing the old one.","In tests, generate fresh threadId/resourceId pairs per user instead of constants."],"exampleFix":"// before\nconst threadId = 'shared-demo-thread'; // reused across users\n// after\nconst threadId = `user-${resourceId}-main`; // unique per resource","handlingStrategy":"validation","validationCode":"const thread = await memory.getThreadById({ threadId });\nif (thread && thread.resourceId && thread.resourceId !== resourceId) {\n  throw new Error(`Thread ${threadId} belongs to ${thread.resourceId}, not ${resourceId}`);\n}","typeGuard":"const threadBelongsToResource = (\n  thread: { resourceId?: string | null },\n  resourceId: string,\n): boolean => !thread.resourceId || thread.resourceId === resourceId;","tryCatchPattern":"try {\n  await agent.generate(input, { memory: { thread, resource } });\n} catch (e) {\n  if (e instanceof Error && e.message.includes('resourceId does not match')) {\n    // create a new thread for this resource instead of reusing\n  }\n}","preventionTips":["NamespacethreadIds by resource, e.g. `${resourceId}-${conversationId}`.","Never accept threadId from untrusted client input; look it up server-side keyed by the authenticated resource.","Add an assertion in tests that thread.resourceId === resourceId before reuse.","Handle account re-keying by migrating or recreating threads."],"tags":["memory","working-memory","resource-mismatch","multi-tenancy"],"backgroundTag":"resource-ownership-mismatch","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}