{"record":{"id":"10ae2892a85a8f80","repo":"mastra-ai/mastra","slug":"subconscious-learn-requires-organizationid-in-the","errorCode":null,"errorMessage":"Subconscious learn requires organizationId in the request context.","messagePattern":"Subconscious learn requires organizationId in the request context\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"critical","filePath":"packages/memory/src/processors/observational-memory/subconscious/learn.ts","lineNumber":34,"sourceCode":"import { resolveKnowledgeResourceId } from './scope';\nimport type { ResolvedSubconsciousAgent, ResolvedSubconsciousConfig } from './types';\n\nconst LEARN_AGENT = 'learn';\nconst ULID_ALPHABET = '0123456789ABCDEFGHJKMNPQRSTVWXYZ';\nconst DEFAULT_INSTRUCTIONS = `Learn reusable skills from the full pre-reflection observations and pending knowledge records.\n\nA skill is a repeatable procedure with ordered actions, a trigger or context, and a success or recovery outcome. Do not learn one-off events, isolated preferences, knowledge records, or procedures supported by fewer than two distinct pending knowledge records. Search existing kind:skill nodes by exact name before writing so updates extend one skill rather than creating duplicates.\n\nUse knowledge_record_skill for every skill creation or evidence update. It validates the evidence frontier and writes retry-safe evidence. You may use the other scoped knowledge tools for research and maintenance, but never restore deleted records, invent provenance or versions, or write outside the source scope.\n\nProcess pending records in ID order. End with <learning-complete through=\"RECORD_ID\" /> naming the last pending record you reviewed, even when no reusable skill was found. Acknowledge only records you fully reviewed.`;\n\ntype LearnerState = { recordedName?: string };\n\nfunction resolveScope(context: ReflectionCommittedContext): KnowledgeScope {\n  const organizationId = context.requestContext?.get('organizationId');\n  if (typeof organizationId !== 'string' || !organizationId.trim()) {\n    throw new Error('Subconscious learn requires organizationId in the request context.');\n  }\n  return canonicalizeKnowledgeScope([\n    `org:${organizationId}`,\n    `resource:${resolveKnowledgeResourceId(context.requestContext, context.resourceId)}`,\n    `thread:${context.parentThreadId}`,\n  ]);\n}\n\n/** Upper bound on records pulled into a single reflection prompt; `hasMore` signals truncation. */\nconst MAX_WORKLIST_RECORDS = 1000;\n\nasync function readWorklist(store: KnowledgeStorage, sourceThreadId: string, scope: KnowledgeScope, after?: string) {\n  const records: KnowledgeRecord[] = [];\n  let cursor = after;\n  do {\n    const page = await store.knowledgeBySource({ sourceThreadId, scope, after: cursor, limit: 100 });\n    records.push(...page.records);\n    cursor = page.nextCursor;","sourceCodeStart":16,"sourceCodeEnd":52,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/packages/memory/src/processors/observational-memory/subconscious/learn.ts#L16-L52","documentation":"The subconscious learner derives the knowledge scope from the request context, and organizationId is mandatory: the scope is anchored at org:<organizationId>. If requestContext.get('organizationId') is missing, empty, or not a string, resolveScope throws before any learning happens. This guarantees knowledge is always scoped to a tenant and never leaked across organizations.","triggerScenarios":"Running an agent/processor with a requestContext that lacks an organizationId entry, passing an empty string or whitespace-only value, or constructing the ReflectionCommittedContext manually without requestContext.","commonSituations":"Local development where multi-tenant context plumbing is skipped; background jobs that build contexts by hand; upgrading the observational-memory processor without updating call sites to supply tenant context.","solutions":["Pass requestContext: new Map([['organizationId', orgId]]) (or equivalent) with a non-empty string organizationId when running the processor/agent","Audit context-construction code paths so every invocation includes the tenant's organizationId","Add a startup assertion/log for missing organizationId in dev to fail fast before reflection"],"exampleFix":"// before\nawait memory.processReflection({ context: { resourceId, parentThreadId } });\n// after\nawait memory.processReflection({\n  context: { resourceId, parentThreadId, requestContext: new Map([['organizationId', 'org_123']]) },\n});","handlingStrategy":"validation","validationCode":"function assertOrgContext(requestContext?: Map<string, unknown> | undefined): string {\n  const orgId = requestContext?.get('organizationId');\n  if (typeof orgId !== 'string' || !orgId.trim()) throw new Error('organizationId required in requestContext');\n  return orgId;\n}","typeGuard":"function hasOrgContext(ctx: unknown): ctx is { requestContext: Map<string, string> & { get(k: 'organizationId'): string } } {\n  const orgId = (ctx as any)?.requestContext?.get?.('organizationId');\n  return typeof orgId === 'string' && !!orgId.trim();\n}","tryCatchPattern":"try {\n  await memory.processReflection(args);\n} catch (e) {\n  if (e instanceof Error && e.message.includes('requires organizationId')) {\n    // attach tenant context and requeue the reflection\n  } else throw e;\n}","preventionTips":["Always set organizationId in requestContext for multi-tenant runs","Reject empty/whitespace org IDs at context construction time","Add an integration test asserting reflection fails fast without tenant context"],"tags":["memory","missing-context","multi-tenancy"],"backgroundTag":"missing-required-context-field","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}