mastra-ai/mastra · error

Subconscious requires threadId to derive scoped knowledge.

Error message

Subconscious requires threadId to derive scoped knowledge.

What it means

Knowledge scopes include the conversation thread; requireScopeContext throws when context.threadId is falsy, since knowledge captured without a thread can't be scoped/attributed to a conversation.

Source

Thrown at packages/memory/src/processors/observational-memory/subconscious/capture.ts:87

const CAPTURE_REASON_INSTRUCTIONS = `Every record requires a reason: the concrete why behind capturing it, in one short sentence - what it cost to learn or when it will matter again (and for pinned records, why it must stay in context). Never write generic filler such as "seemed relevant" or "useful context".`;

function clampScope(level: KnowledgeScopeLevel, ceiling?: KnowledgeScopeLevel): KnowledgeScopeLevel {
  return ceiling && SCOPE_ORDER[level] < SCOPE_ORDER[ceiling] ? ceiling : level;
}

function requireScopeContext(context: ExtractorRuntimeContext): KnowledgeScope {
  const organizationId = context.requestContext?.get('organizationId');
  if (typeof organizationId !== 'string' || !organizationId.trim()) {
    throw new Error(
      'Subconscious requires requestContext.organizationId to derive scoped knowledge. Set organizationId on the request context for this conversation.',
    );
  }
  const resourceId = resolveKnowledgeResourceId(context.requestContext, context.resourceId);
  if (!resourceId) {
    throw new Error('Subconscious requires resourceId to derive scoped knowledge.');
  }
  if (!context.threadId) {
    throw new Error('Subconscious requires threadId to derive scoped knowledge.');
  }
  return [`org:${organizationId}`, `resource:${resourceId}`, `thread:${context.threadId}`];
}

async function getKnowledgeStore(context: ExtractorRuntimeContext): Promise<KnowledgeStorage> {
  if (!context.memory) throw new Error('Subconscious capture requires an active Memory instance.');
  const store = await context.memory.storage.getStore('knowledge');
  if (!store) {
    throw new Error(
      'Subconscious requires a knowledge storage domain. Configure a storage adapter that provides stores.knowledge.',
    );
  }
  return store;
}

function parseWhen(value: string | undefined): Date | undefined {
  if (!value) return undefined;
  const when = new Date(value);

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Ensure a thread exists and its id is passed to the memory call ({ threadId })
  2. Create a thread first (memory.createThread) for background jobs that need capture
  3. Guard processor wiring so capture only runs in thread-scoped conversations

Example fix

// before
await memory.capture({ organizationId, resourceId });
// after
const thread = await memory.createThread({ resourceId: 'user_42' });
await memory.capture({ organizationId, resourceId: 'user_42', threadId: thread.id });
Defensive patterns

Strategy: validation

Validate before calling

if (!threadId) throw new Error('threadId required for Subconscious capture');

Type guard

const hasThread = (p: { threadId?: string }) => typeof p.threadId === 'string' && p.threadId.length > 0;

Try / catch

try { await memory.capture(params) } catch (e) { if (String(e).includes('threadId')) { await ensureThreadThenRetry(); } else throw e; }

Prevention

When it happens

Trigger: Subconscious capture runs with organizationId and resourceId present but threadId missing — e.g. invoking memory processors outside a thread-backed conversation.

Common situations: Background/batch jobs invoking capture without creating a thread; custom pipelines calling processors directly; thread creation skipped before the first generate.

Related errors


AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30). Data as JSON: /api/errors/898661ac85e260c9. Report an issue: GitHub.