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
- Add an onExtracted: async (result, ctx) => {...} function to the capture entry.
- Remove the schema if you want default capture behavior with no custom hook.
- 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
- Always pair schema with onExtracted in a single factory helper.
- Type your capture entry so onExtracted is required when schema is present.
- Unit-test construction with your real config.
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
- Custom Subconscious observation agent "${name}" requires sch
- Google RBAC roleMapping is required.
- Cookie password must be at least 32 characters. Set OKTA_COO
- heartbeatMs must be a finite number no greater than ${MAX_TI
- Expected a PEM-encoded public key or certificate string for
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/8cee42bc6ef9b403.
Report an issue: GitHub.