mastra-ai/mastra · error · Error
Extractor slug "${slug}" is reserved by Observational Memory
Error message
Extractor slug "${slug}" is reserved by Observational Memory. What it means
Extractor names are slugified into kebab-case and used as XML tags in prompts and metadata keys. Observational Memory reserves a set of tags for its own protocol (`observations`, `observation`, `extracted-values`, `thread`, `message`, `messages`, `conversation`, `history`, `system`, `user`, `assistant`, `tool`, plus built-in extractor slugs like `thread-title`) and refuses user extractors whose slug collides, to avoid corrupting the prompt/output parsing.
Source
Thrown at packages/memory/src/processors/observational-memory/extractor.ts:162
if (!name) {
throw new Error('Extractor name is required.');
}
if (!isHook && !config.instructions) {
throw new Error(`Extractor "${name}" must include instructions.`);
}
if (instructions !== undefined && !instructions) {
throw new Error(`Extractor "${name}" must include instructions.`);
}
if (isHook && !config.onExtracted) {
throw new Error(`Hook extractor "${name}" must include an onExtracted handler.`);
}
if (isHook && (config.instructions || config.schema)) {
throw new Error(`Hook extractor "${name}" cannot include instructions or a schema.`);
}
assertValidSlug(slug, name);
if (!internal && RESERVED_XML_TAGS.has(slug)) {
throw new Error(`Extractor slug "${slug}" is reserved by Observational Memory.`);
}
this.name = name;
this.slug = slug;
this.instructionsConfig = config.instructions ?? '';
this.schemaConfig = config.schema;
this.instructions = instructions ?? '';
this.schema = (
isHook ? z.unknown() : typeof config.schema === 'function' ? z.string() : (config.schema ?? z.string())
) as z.ZodType<T>;
this.mode = isHook ? 'hook' : internal || !config.schema ? 'inline' : 'structured';
this.includePreviousExtraction = isHook ? false : (config.includePreviousExtraction ?? true);
this.metadataKeyPath = isHook ? false : (config.metadataKeyPath ?? `extracted.${slug}`);
this.onExtracted = config.onExtracted;
this.retryStructuredExtractionOnEmptyObject = config.retryStructuredExtractionOnEmptyObject ?? false;
this.internal = internal;
}
View on GitHub (pinned to 75dd419e61)
Solutions
- Rename the extractor so its kebab-case slug does not match a reserved tag (e.g. 'Observations' → 'key-observations').
- Avoid built-in extractor slugs: current-task, suggested-response, thread-title.
- Check the slug with slugifyExtractorName(name) before constructing.
Example fix
// before
new Extractor({ name: 'Thread Title', instructions: '...' });
// after
new Extractor({ name: 'Session Title', instructions: '...' }); Defensive patterns
Strategy: validation
Validate before calling
const RESERVED = new Set(['observations','observation','extracted-values','thread','message','messages','conversation','history','system','user','assistant','tool','current-task','suggested-response','thread-title']);
const slug = slugifyExtractorName(name);
if (RESERVED.has(slug)) throw new Error(`Name "${name}" collides with reserved slug "${slug}"`); Type guard
function isSafeExtractorName(name) {
return !RESERVED.has(slugifyExtractorName(name));
} Try / catch
try {
const extractor = new Extractor({ name, instructions });
} catch (e) {
if (e.message.includes('is reserved by Observational Memory')) {
logger.error('Reserved extractor slug', { name });
} else { throw e; }
} Prevention
- Prefix extractor names with a domain word (e.g. 'crm-user-goals') to avoid generic collisions.
- Never name extractors after the built-ins: current-task, suggested-response, thread-title.
- Pre-check names with slugifyExtractorName in user-facing config validation.
When it happens
Trigger: Naming an extractor 'Observations', 'Thread Title', 'User', 'System', 'Extracted Values', etc. — any name whose slugified form matches a reserved tag; e.g. name: 'USER' or 'my observations' → slug 'observations'? no ('my-observations' ok), but name 'Observations!' slugifies to 'observations' and throws.
Common situations: Trying to override built-in extractors (current-task, suggested-response, thread-title) by reusing their names; naming extractors after protocol tags; generic names like 'History' or 'Messages' that collide.
Related errors
- Extractor slug "${extractor.slug}" is reserved by Observatio
- Extractor name "${name}" must produce a non-empty slug.
- Extractor name "${name}" produced invalid slug "${slug}".
- Extractor name is required.
- Duplicate extractor slug "${extractor.slug}" from "${previou
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/0b8557401792e17a.
Report an issue: GitHub.