mastra-ai/mastra · error · Error

A custom capture schema requires an onExtracted hook that ha

Error message

A custom capture schema requires an onExtracted hook that handles its output.

What it means

A capture entry may declare a custom output schema, but a schema alone is unusable: the library requires an onExtracted callback that knows how to handle the schema's output. If entry.schema is set/truthy and onExtracted is missing or not a function, the constructor throws.

Source

Thrown at packages/memory/src/processors/observational-memory/subconscious/index.ts:208

  }

  #validateObservationEntry(entry: SubconsciousObservationEntry): void {
    const name = entryName(entry);
    if (typeof entry === 'string') {
      if (!BUILT_IN_OBSERVATION.has(name)) throw new Error(`Unknown Subconscious observation agent: ${name}`);
      return;
    }
    if (BUILT_IN_OBSERVATION.has(name)) {
      if (name === 'capture') {
        if ('model' in entry || 'maxSteps' in entry) {
          throw new Error('Subconscious capture shares the Observer model and does not accept model or maxSteps.');
        }
        if (
          'schema' in entry &&
          entry.schema &&
          (!('onExtracted' in entry) || typeof entry.onExtracted !== 'function')
        ) {
          throw new Error('A custom capture schema requires an onExtracted hook that handles its output.');
        }
      }
      return;
    }
    if ('model' in entry || 'maxSteps' in entry) {
      throw new Error(
        `Subconscious observation extractor "${name}" shares the Observer model and does not accept model or maxSteps.`,
      );
    }
    if (!('schema' in entry) || !entry.schema || !('onExtracted' in entry) || typeof entry.onExtracted !== 'function') {
      throw new Error(`Custom Subconscious observation agent "${name}" requires schema and onExtracted.`);
    }
  }

  #validateReflectionEntry(entry: SubconsciousReflectionEntry): void {
    const name = entryName(entry);
    if (typeof entry === 'string') {
      if (!BUILT_IN_REFLECTION.has(name)) throw new Error(`Unknown Subconscious reflection agent: ${name}`);

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Add an onExtracted: async (result, ctx) => {...} function to the capture entry.
  2. Remove the schema if you want default capture behavior with no custom hook.
  3. Fix the property name/spelling so onExtracted is actually a function.

Example fix

// before
{ name: 'capture', schema: InsightSchema }
// after
{ name: 'capture', schema: InsightSchema, onExtracted: async (insight) => { await save(insight); } }
Defensive patterns

Strategy: validation

Validate before calling

function captureEntryIsValid(e: { schema?: unknown; onExtracted?: unknown }): boolean {
  return !e.schema || typeof e.onExtracted === 'function';
}
if (!captureEntryIsValid(myCapture)) throw new Error('schema requires onExtracted');

Type guard

function hasOnExtracted<T extends { onExtracted?: unknown }>(e: T): e is T & { onExtracted: (r: unknown) => Promise<void> | void } {
  return typeof e.onExtracted === 'function';
}

Try / catch

try {
  sub = new Subconscious({ observation: [entry] });
} catch (e) {
  if (e instanceof Error && e.message.includes('onExtracted hook')) {
    console.error('capture schema provided without onExtracted handler');
  } else throw e;
}

Prevention

When it happens

Trigger: new Subconscious({ observation: [{ name: 'capture', schema: ZodSchema }] }) with no onExtracted, or onExtracted set to a non-function (e.g. undefined, a string).

Common situations: Adding a schema to constrain capture output but forgetting the handler; passing onExtracted: undefined via spread config; typos like onExtract or onextracted.

Related errors


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