{"record":{"id":"16c1c72258382262","repo":"mastra-ai/mastra","slug":"knowledge-tools-require-an-active-threadid","errorCode":null,"errorMessage":"Knowledge tools require an active threadId.","messagePattern":"Knowledge tools require an active threadId\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/memory/src/processors/observational-memory/subconscious/knowledge-tools.ts","lineNumber":39,"sourceCode":"    getStore(name: 'knowledge'): Promise<KnowledgeStorage | undefined>;\n  };\n  getKnowledgeSemanticIndex(): Promise<KnowledgeSemanticIndexCoordinator>;\n};\n\ntype KnowledgeToolContext = {\n  agent?: { threadId?: string; resourceId?: string };\n  requestContext?: { get(key: string): unknown };\n};\n\nfunction resolveScope(context: KnowledgeToolContext | undefined): KnowledgeScope {\n  const organizationId = context?.requestContext?.get('organizationId');\n  const resourceId = resolveKnowledgeResourceId(context?.requestContext, context?.agent?.resourceId);\n  const threadId = context?.agent?.threadId;\n  if (typeof organizationId !== 'string' || !organizationId.trim()) {\n    throw new Error('Knowledge tools require requestContext.organizationId.');\n  }\n  if (!resourceId) throw new Error('Knowledge tools require an active resourceId.');\n  if (!threadId) throw new Error('Knowledge tools require an active threadId.');\n  return [`org:${organizationId}`, `resource:${resourceId}`, `thread:${threadId}`];\n}\n\nasync function getKnowledgeStore(memory: KnowledgeToolsMemory): Promise<KnowledgeStorage> {\n  const store = await memory.storage.getStore('knowledge');\n  if (!store) throw new Error('Knowledge tools require a configured knowledge storage domain.');\n  return store;\n}\n\nfunction normalizeLimit(limit: number | undefined): number {\n  return Math.min(Math.max(limit ?? DEFAULT_LIMIT, 1), MAX_LIMIT);\n}\n\nfunction serializeRecord(record: KnowledgeRecord) {\n  return {\n    id: record.id,\n    text: record.text,\n    scope: record.scope,","sourceCodeStart":21,"sourceCodeEnd":57,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/packages/memory/src/processors/observational-memory/subconscious/knowledge-tools.ts#L21-L57","documentation":"The observational-memory knowledge tools derive their visibility scope from three coordinates: requestContext.organizationId, agent.resourceId, and agent.threadId. Before any knowledge tool runs, resolveScope builds the scope array [`org:...`, `resource:...`, `thread:...`] and refuses to proceed when the threadId coordinate is missing, because knowledge is always resolved within an active conversation thread. The library throws this to prevent tools from silently operating against an unbounded or ambiguous scope.","triggerScenarios":"Invoking any knowledge tool (knowledge_read, knowledge_search, etc.) via createKnowledgeTools when context.agent.threadId is undefined — e.g. calling the tool outside a memory-backed agent run, or in a unit test harness that stubs context without a threadId.","commonSituations":"Developers testing knowledge tools in isolation with a hand-rolled context object that omits threadId; running the curator/learner agent outside a normal Mastra agent loop; calling tools during startup or background jobs where no thread has been created yet.","solutions":["Run the agent through the normal Mastra memory flow so a thread is created and context.agent.threadId is populated before tools execute.","If calling tools manually, pass a context object with agent: { threadId: '<existing-thread-id>', resourceId: '<resource-id>' }.","Create a thread first (via memory storage or agent.memory APIs) and use its ID.","Verify requestContext also supplies organizationId and resourceId, since those are checked first and their absence masks later checks."],"exampleFix":"// before\nconst tools = createKnowledgeTools(memory);\nawait tools.knowledge_read.execute({ id: 'node-1' }, {} as any);\n// after\nawait tools.knowledge_read.execute({ id: 'node-1' }, {\n  agent: { threadId: 'thread_123', resourceId: 'user_42' },\n  requestContext: { get: (k) => (k === 'organizationId' ? 'org_1' : undefined) },\n} as any);","handlingStrategy":"validation","validationCode":"const ctx = context as { agent?: { threadId?: string; resourceId?: string }; requestContext?: { get(k: string): unknown } };\nif (!ctx?.agent?.threadId) {\n  throw new Error('Refusing to invoke knowledge tools: no active threadId.');\n}","typeGuard":"function hasThreadContext(c: unknown): c is { agent: { threadId: string; resourceId: string }; requestContext: { get(k: string): unknown } } {\n  const a = (c as any)?.agent;\n  return typeof a?.threadId === 'string' && a.threadId.trim() !== '' && typeof a?.resourceId === 'string';\n}","tryCatchPattern":"try {\n  return await tools.knowledge_read.execute(args, ctx);\n} catch (e) {\n  if (e instanceof Error && e.message.includes('active threadId')) {\n    return { error: 'no-active-thread', hint: 'Run inside a memory-backed agent thread.' };\n  }\n  throw e;\n}","preventionTips":["Always invoke knowledge tools inside a memory-backed agent run so threadId is set.","When testing manually, stub agent: { threadId, resourceId } in the context.","Create the thread before wiring knowledge tools.","Assert all three scope inputs (organizationId, resourceId, threadId) in a pre-flight helper."],"tags":["knowledge-tools","missing-thread","scope-resolution","mastra-memory"],"backgroundTag":"missing-required-context-id","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}