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

  1. 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]).
  2. Ensure the value is a string, not a number or object; coerce with String(value) first if needed.
  3. Shorten the slug to 64 characters or fewer.
  4. 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

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


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)