{"record":{"id":"0b8557401792e17a","repo":"mastra-ai/mastra","slug":"extractor-slug-slug-is-reserved-by-observatio","errorCode":null,"errorMessage":"Extractor slug \"${slug}\" is reserved by Observational Memory.","messagePattern":"Extractor slug \"(.+?)\" is reserved by Observational Memory\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/memory/src/processors/observational-memory/extractor.ts","lineNumber":162,"sourceCode":"\n    if (!name) {\n      throw new Error('Extractor name is required.');\n    }\n    if (!isHook && !config.instructions) {\n      throw new Error(`Extractor \"${name}\" must include instructions.`);\n    }\n    if (instructions !== undefined && !instructions) {\n      throw new Error(`Extractor \"${name}\" must include instructions.`);\n    }\n    if (isHook && !config.onExtracted) {\n      throw new Error(`Hook extractor \"${name}\" must include an onExtracted handler.`);\n    }\n    if (isHook && (config.instructions || config.schema)) {\n      throw new Error(`Hook extractor \"${name}\" cannot include instructions or a schema.`);\n    }\n    assertValidSlug(slug, name);\n    if (!internal && RESERVED_XML_TAGS.has(slug)) {\n      throw new Error(`Extractor slug \"${slug}\" is reserved by Observational Memory.`);\n    }\n\n    this.name = name;\n    this.slug = slug;\n    this.instructionsConfig = config.instructions ?? '';\n    this.schemaConfig = config.schema;\n    this.instructions = instructions ?? '';\n    this.schema = (\n      isHook ? z.unknown() : typeof config.schema === 'function' ? z.string() : (config.schema ?? z.string())\n    ) as z.ZodType<T>;\n    this.mode = isHook ? 'hook' : internal || !config.schema ? 'inline' : 'structured';\n    this.includePreviousExtraction = isHook ? false : (config.includePreviousExtraction ?? true);\n    this.metadataKeyPath = isHook ? false : (config.metadataKeyPath ?? `extracted.${slug}`);\n    this.onExtracted = config.onExtracted;\n    this.retryStructuredExtractionOnEmptyObject = config.retryStructuredExtractionOnEmptyObject ?? false;\n    this.internal = internal;\n  }\n","sourceCodeStart":144,"sourceCodeEnd":180,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/packages/memory/src/processors/observational-memory/extractor.ts#L144-L180","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nnew Extractor({ name: 'Thread Title', instructions: '...' });\n// after\nnew Extractor({ name: 'Session Title', instructions: '...' });","handlingStrategy":"validation","validationCode":"const RESERVED = new Set(['observations','observation','extracted-values','thread','message','messages','conversation','history','system','user','assistant','tool','current-task','suggested-response','thread-title']);\nconst slug = slugifyExtractorName(name);\nif (RESERVED.has(slug)) throw new Error(`Name \"${name}\" collides with reserved slug \"${slug}\"`);","typeGuard":"function isSafeExtractorName(name) {\n  return !RESERVED.has(slugifyExtractorName(name));\n}","tryCatchPattern":"try {\n  const extractor = new Extractor({ name, instructions });\n} catch (e) {\n  if (e.message.includes('is reserved by Observational Memory')) {\n    logger.error('Reserved extractor slug', { name });\n  } else { throw e; }\n}","preventionTips":["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."],"tags":["naming","reserved-words","observational-memory"],"backgroundTag":"reserved-identifier-conflict","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}