affaan-m/ECC · error
must be a lowercase letters/numbers slug.
Error message
${label} must be a lowercase letters/numbers slug. What it means
validateSlug enforces that a slug-like field (e.g. source harness, tag scope identifiers) is a non-empty string of at most 64 characters matching /^[a-z0-9][a-z0-9._-]{0,63}$/ — lowercase letters, digits, dots, underscores, hyphens only, starting with a letter or digit. The library throws this to keep slugs safe for use as file names and identifiers in the memory vault, blocking uppercase text, spaces, and path characters like '/'.
Solutions
- Lowercase the value and replace every character outside [a-z0-9._-] with '-' (trim leading/trailing separators is optional, first char must be [a-z0-9]).
- Ensure the value is a string, not a number or object; coerce with String(value) first if needed.
- Shorten the slug to 64 characters or fewer.
- If the value is meant to be an id, use validateMemoryId's mem_ format instead.
Example fix
// before
normalizeMemory({ sourceHarness: 'Claude Code', ... })
// after
normalizeMemory({ sourceHarness: 'claude-code', ... }) Defensive patterns
Strategy: validation
Validate before calling
function isValidSlug(v) { return typeof v === 'string' && /^[a-z0-9][a-z0-9._-]{0,63}$/.test(v); }
if (!isValidSlug(harness)) throw new Error('bad slug: ' + harness); Type guard
const isSlug = (v) => typeof v === 'string' && /^[a-z0-9][a-z0-9._-]{0,63}$/.test(v); Try / catch
try { normalizeMemory(mem); } catch (e) { if (e.message.includes('must be a lowercase letters/numbers slug')) { mem.sourceHarness = slugify(mem.sourceHarness); } else throw e; } Prevention
- Slugify all identifiers at ingestion: v.toLowerCase().replace(/[^a-z0-9._-]+/g, '-')
- Add a unit test for every config field that feeds a slug
- Never build slugs from user-visible names without normalization
- Lint frontmatter files with the same regex in CI
When it happens
Trigger: Calling normalizeMemory with a source_harness/target harness value, or any code path calling validateSlug(value, label) where value is e.g. 'Claude Code' (uppercase/space), '/etc/passwd', '', or a 65+ character string.
Common situations: Hand-written frontmatter with CamelCase harness names like 'Claude-Code'; slugs copy-pasted from URLs containing uppercase or '%20'; slugs accidentally containing a slash producing a path-traversal risk; a locale or encoder uppercasing the value.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
- has too many values (maximum ).
- is too long (maximum characters).
- must be a non-empty string.
- must be an array.
- must be an ISO-8601 timestamp.
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/7932c0548219bc85.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/lib/memory-vault-format.js:102
}
if (hasUnsafeControlCharacters(normalized)) {
throw new Error(`${label} must not contain control or bidirectional formatting characters.`);
}
return normalized;
}
function validateEnum(value, allowed, label) {
const normalized = asNonEmptyString(value, label, 64);
if (!allowed.includes(normalized)) {
throw new Error(`${label} must be one of: ${allowed.join(', ')}.`);
}
return normalized;
}
function validateSlug(value, label) {
const normalized = asNonEmptyString(value, label, 64);
if (!SLUG_PATTERN.test(normalized)) {
throw new Error(`${label} must be a lowercase letters/numbers slug.`);
}
return normalized;
}
function validateMemoryId(value) {
const normalized = asNonEmptyString(value, 'memory id', 132);
if (!MEMORY_ID_PATTERN.test(normalized)) {
throw new Error('memory id must match mem_<lowercase-id> and cannot contain a path.');
}
return normalized;
}
function uniqueStrings(values, { label, limit, validator }) {
if (!Array.isArray(values)) {
throw new Error(`${label} must be an array.`);
}
if (values.length > limit) {
throw new Error(`${label} has too many values (maximum ${limit}).`);View on GitHub (pinned to 8321021c54)