{"record":{"id":"9a7bf85cc2f18545","repo":"mastra-ai/mastra","slug":"extractor-slug-extractor-slug-is-reserved-by","errorCode":null,"errorMessage":"Extractor slug \"${extractor.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":222,"sourceCode":"      },\n      this.internal,\n    );\n  }\n}\n\nexport async function resolveExtractors(\n  extractors: readonly Extractor<any>[],\n  context: ExtractorRuntimeContext,\n): Promise<Extractor<any>[]> {\n  return Promise.all(extractors.map(extractor => extractor.resolve(context)));\n}\n\nexport function validateExtractorList(extractors: readonly Extractor<any>[]): Extractor<any>[] {\n  const seen = new Map<string, string>();\n  for (const extractor of extractors) {\n    assertValidSlug(extractor.slug, extractor.name);\n    if (!extractor.internal && RESERVED_XML_TAGS.has(extractor.slug)) {\n      throw new Error(`Extractor slug \"${extractor.slug}\" is reserved by Observational Memory.`);\n    }\n    const previous = seen.get(extractor.slug);\n    if (previous) {\n      throw new Error(`Duplicate extractor slug \"${extractor.slug}\" from \"${previous}\" and \"${extractor.name}\".`);\n    }\n    seen.set(extractor.slug, extractor.name);\n  }\n  return [...extractors];\n}\n\nfunction isJsonLike(value: string): boolean {\n  return /^(?:[\\[{\"-]|\\d|true\\b|false\\b|null\\b)/.test(value.trim());\n}\n\nfunction candidateValues(raw: string): unknown[] {\n  const trimmed = raw.trim();\n  const candidates: unknown[] = [];\n  const add = (value: unknown) => {","sourceCodeStart":204,"sourceCodeEnd":240,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/packages/memory/src/processors/observational-memory/extractor.ts#L204-L240","documentation":"`validateExtractorList` runs when extractor lists are composed for a processor and re-checks each extractor's slug against the reserved XML tag set (observations, observation, extracted-values, thread, message, messages, conversation, history, system, user, assistant, tool, and built-in slugs current-task/suggested-response/thread-title). Non-internal extractors with a reserved slug are rejected because their output tags would collide with Observational Memory's own parsing protocol. This is a defense-in-depth check at composition time, in addition to the constructor guard.","triggerScenarios":"Composing a list containing an extractor constructed with `internal = true` bypass semantics won't help here — any user-supplied extractor whose slugified name matches a reserved tag (e.g. 'User', 'History', 'Thread Title'); manually constructed Extractor instances passed via ObservationalMemory config; extractors produced by a helper/factory that assembles names dynamically and happens to hit a reserved word.","commonSituations":"Dynamically generated extractor names from user config; trying to shadow built-in extractors; a name like 'Messages!' that slugifies to 'messages'.","solutions":["Rename the extractor so its slug differs from all reserved tags.","If listing extractors from user input, filter/validate slugs against RESERVED_XML_TAGS before composing the list.","Reuse isBuiltInExtractorSlug / slugifyExtractorName to pre-check names."],"exampleFix":"// before\nnew Extractor({ name: 'Messages', instructions: 'List key messages' });\n// after\nnew Extractor({ name: 'Key Messages', instructions: 'List key messages' });","handlingStrategy":"validation","validationCode":"import { slugifyExtractorName, isBuiltInExtractorSlug } from '@mastra/memory/processors/observational-memory';\nfor (const e of extractors) {\n  if (!e.internal && RESERVED_XML_TAGS.has(e.slug)) throw new Error(`Reserved slug: ${e.slug}`);\n}","typeGuard":"function allSlugsSafe(list) {\n  return list.every(e => e.internal || !RESERVED.has(e.slug));\n}","tryCatchPattern":"try {\n  validateExtractorList(extractors);\n} catch (e) {\n  if (e.message.includes('is reserved by Observational Memory')) {\n    logger.error('Extractor list contains reserved slug', { msg: e.message });\n  } else { throw e; }\n}","preventionTips":["Run validateExtractorList in your own config-loading layer before handing extractors to the processor.","Sanitize user-supplied extractor names against the reserved tag list at ingestion time.","Add a unit test asserting your production extractor list validates cleanly."],"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"}