{"record":{"id":"175ba93fbbf2a146","repo":"mastra-ai/mastra","slug":"extractor-name-name-produced-invalid-slug","errorCode":null,"errorMessage":"Extractor name \"${name}\" produced invalid slug \"${slug}\".","messagePattern":"Extractor name \"(.+?)\" produced invalid slug \"(.+?)\"\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/memory/src/processors/observational-memory/extractor.ts","lineNumber":120,"sourceCode":"  }\n\n  return normalized.endsWith('-') ? normalized.slice(0, -1) : normalized;\n}\n\nfunction assertValidSlug(slug: string, name: string): void {\n  if (!slug) {\n    throw new Error(`Extractor name \"${name}\" must produce a non-empty slug.`);\n  }\n  const first = slug.charCodeAt(0);\n  const last = slug.charCodeAt(slug.length - 1);\n  const startsWithLetter = first >= 97 && first <= 122;\n  const endsWithLetterOrNumber = (last >= 97 && last <= 122) || (last >= 48 && last <= 57);\n  const hasOnlySlugCharacters = [...slug].every(char => {\n    const code = char.charCodeAt(0);\n    return (code >= 97 && code <= 122) || (code >= 48 && code <= 57) || char === '-';\n  });\n  if (!startsWithLetter || !endsWithLetterOrNumber || !hasOnlySlugCharacters) {\n    throw new Error(`Extractor name \"${name}\" produced invalid slug \"${slug}\".`);\n  }\n}\n\nexport class Extractor<T = unknown> {\n  readonly name: string;\n  readonly slug: string;\n  readonly instructions: string;\n  readonly schema: z.ZodType<T>;\n  readonly mode: ExtractorMode;\n  readonly includePreviousExtraction: boolean;\n  readonly metadataKeyPath: string | false;\n  readonly onExtracted?: ExtractorConfig<T>['onExtracted'];\n  readonly retryStructuredExtractionOnEmptyObject: boolean;\n  /** @internal */\n  readonly internal: boolean;\n  private readonly instructionsConfig: ExtractorConfigValue<string>;\n  private readonly schemaConfig?: ExtractorConfigValue<z.ZodType<T> | undefined>;\n","sourceCodeStart":102,"sourceCodeEnd":138,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/packages/memory/src/processors/observational-memory/extractor.ts#L102-L138","documentation":"After slugification, assertValidSlug enforces that the slug starts with a lowercase letter, ends with a letter or digit, and contains only lowercase letters, digits, and hyphens. A slug violating any of these rules throws this error naming both the source extractor name and the resulting slug.","triggerScenarios":"Registering an Extractor whose name slugifies to something like \"-foo-\" (leading/trailing hyphen), \"123abc\" (starts with a digit), or containing invalid characters that survived normalization.","commonSituations":"Names starting with numbers (e.g. \"3rd-party-flags\"); names with underscores converted oddly; programmatically generated names built from enums or IDs that begin with digits; hyphen-only edges from names like \"foo -\".","solutions":["Rename the extractor so its slug starts with a letter and ends with a letter/digit (e.g. 'third-party-flags' instead of '3rd-party-flags')","Prefix numeric or symbol-leading names with a word ('extractor-3rd-party')","Pre-validate names with a regex like /^[a-z][a-z0-9-]*[a-z0-9]$/ before constructing extractors"],"exampleFix":"// before\nnew Extractor({ name: '3rd-party-flags', instructions: '...' });\n\n// after\nnew Extractor({ name: 'third-party-flags', instructions: '...' });","handlingStrategy":"validation","validationCode":"const SLUG_RE = /^[a-z][a-z0-9-]*[a-z0-9]$/;\nif (!SLUG_RE.test(slugifyExtractorName(name))) {\n  throw new Error(`Extractor name \"${name}\" must slugify to a valid slug`);\n}","typeGuard":null,"tryCatchPattern":"try {\n  validateExtractorList(extractors);\n} catch (err) {\n  if (err instanceof Error && err.message.includes('produced invalid slug')) {\n    console.error(err.message); // rename the offending extractor\n  }\n  throw err;\n}","preventionTips":["Never start extractor names with digits or symbols","Validate names against /^[a-z][a-z0-9-]*[a-z0-9]$/ (post-slug) before registering","Standardize extractor naming conventions in a shared constant/config module"],"tags":["observational-memory","extractor","naming","input-validation"],"backgroundTag":"invalid-identifier-name","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}