mastra-ai/mastra · error · Error
Duplicate extractor slug "${extractor.slug}" from "${previou
Error message
Duplicate extractor slug "${extractor.slug}" from "${previous}" and "${extractor.name}". What it means
`validateExtractorList` enforces that every extractor in a composed list has a unique slug, since slugs become XML tags and metadata keys (`extracted.<slug>`); duplicates would make parsed values and persistence ambiguous. When two extractors resolve to the same slug, the error reports both conflicting names.
Source
Thrown at packages/memory/src/processors/observational-memory/extractor.ts:226
}
export async function resolveExtractors(
extractors: readonly Extractor<any>[],
context: ExtractorRuntimeContext,
): Promise<Extractor<any>[]> {
return Promise.all(extractors.map(extractor => extractor.resolve(context)));
}
export function validateExtractorList(extractors: readonly Extractor<any>[]): Extractor<any>[] {
const seen = new Map<string, string>();
for (const extractor of extractors) {
assertValidSlug(extractor.slug, extractor.name);
if (!extractor.internal && RESERVED_XML_TAGS.has(extractor.slug)) {
throw new Error(`Extractor slug "${extractor.slug}" is reserved by Observational Memory.`);
}
const previous = seen.get(extractor.slug);
if (previous) {
throw new Error(`Duplicate extractor slug "${extractor.slug}" from "${previous}" and "${extractor.name}".`);
}
seen.set(extractor.slug, extractor.name);
}
return [...extractors];
}
function isJsonLike(value: string): boolean {
return /^(?:[\[{"-]|\d|true\b|false\b|null\b)/.test(value.trim());
}
function candidateValues(raw: string): unknown[] {
const trimmed = raw.trim();
const candidates: unknown[] = [];
const add = (value: unknown) => {
if (!candidates.some(candidate => Object.is(candidate, value))) {
candidates.push(value);
}
};View on GitHub (pinned to 75dd419e61)
Solutions
- Rename one extractor so each slug is unique.
- Deduplicate the extractor array before passing it to the processor (e.g. by slug via slugifyExtractorName).
- If merging config sources, prefer user-supplied extractors over defaults with the same slug instead of concatenating both.
Example fix
// before
[new Extractor({ name: 'User Goals', ... }), new Extractor({ name: 'user-goals!', ... })]
// after
[new Extractor({ name: 'User Goals', ... }), new Extractor({ name: 'User Milestones', ... })] Defensive patterns
Strategy: validation
Validate before calling
function dedupeBySlug(extractors) {
const seen = new Map();
return extractors.filter(e => {
if (seen.has(e.slug)) return false;
seen.set(e.slug, e.name);
return true;
});
} Type guard
function hasUniqueSlugs(list) {
return new Set(list.map(e => e.slug)).size === list.length;
} Try / catch
try {
validateExtractorList(extractors);
} catch (e) {
if (e.message.includes('Duplicate extractor slug')) {
logger.error('Duplicate extractor slugs in list', { msg: e.message });
} else { throw e; }
} Prevention
- Deduplicate merged extractor lists by slug before composition.
- When merging defaults with user config, let user entries override same-slug defaults instead of concatenating.
- Normalize names early (lowercase, strip punctuation) so accidental collisions are visible in code review.
When it happens
Trigger: Two extractors whose names slugify identically (e.g. 'User Goals' and 'user-goals!' both → 'user-goals'); adding the same Extractor instance twice to the list; dynamically generating extractors with names that collide after slugification.
Common situations: Case/punctuation differences in names that feel distinct but slugify the same; merging extractor arrays from multiple sources (defaults + user config) that both include the same extractor; copy-pasted extractor definitions with only name-case changed.
Related errors
- Extractor name "${name}" must produce a non-empty slug.
- Extractor name "${name}" produced invalid slug "${slug}".
- Extractor name is required.
- Extractor slug "${slug}" is reserved by Observational Memory
- Extractor slug "${extractor.slug}" is reserved by Observatio
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/7d8dff336a81cf50.
Report an issue: GitHub.