{"record":{"id":"7932c0548219bc85","repo":"affaan-m/ECC","slug":"label-must-be-a-lowercase-letters-numbers-slug","errorCode":null,"errorMessage":"${label} must be a lowercase letters/numbers slug.","messagePattern":"(.+?) must be a lowercase letters/numbers slug\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/lib/memory-vault-format.js","lineNumber":102,"sourceCode":"  }\n  if (hasUnsafeControlCharacters(normalized)) {\n    throw new Error(`${label} must not contain control or bidirectional formatting characters.`);\n  }\n  return normalized;\n}\n\nfunction validateEnum(value, allowed, label) {\n  const normalized = asNonEmptyString(value, label, 64);\n  if (!allowed.includes(normalized)) {\n    throw new Error(`${label} must be one of: ${allowed.join(', ')}.`);\n  }\n  return normalized;\n}\n\nfunction validateSlug(value, label) {\n  const normalized = asNonEmptyString(value, label, 64);\n  if (!SLUG_PATTERN.test(normalized)) {\n    throw new Error(`${label} must be a lowercase letters/numbers slug.`);\n  }\n  return normalized;\n}\n\nfunction validateMemoryId(value) {\n  const normalized = asNonEmptyString(value, 'memory id', 132);\n  if (!MEMORY_ID_PATTERN.test(normalized)) {\n    throw new Error('memory id must match mem_<lowercase-id> and cannot contain a path.');\n  }\n  return normalized;\n}\n\nfunction uniqueStrings(values, { label, limit, validator }) {\n  if (!Array.isArray(values)) {\n    throw new Error(`${label} must be an array.`);\n  }\n  if (values.length > limit) {\n    throw new Error(`${label} has too many values (maximum ${limit}).`);","sourceCodeStart":84,"sourceCodeEnd":120,"githubUrl":"https://github.com/affaan-m/ECC/blob/01e15490f04e29cfefe3896951f43db46994d8ee/scripts/lib/memory-vault-format.js#L84-L120","documentation":"Thrown by validateSlug() when the trimmed value does not match SLUG_PATTERN (/^[a-z0-9][a-z0-9._-]{0,63}$/). Slugs must start with a lowercase letter or digit, contain only lowercase alphanumerics, dot, underscore, or hyphen, and be at most 64 chars. Used for tag slugs, target harness identifiers, and similar keyed fields.","triggerScenarios":"Passing 'My Tag', 'tag with space', 'UPPER', a slug starting with a hyphen, or a slug longer than 64 characters.","commonSituations":"User-typed tags with capitalization or spaces; copy-pasting a display name where a slug is required; generated slugs that include forbidden punctuation.","solutions":["Lowercase the value and replace spaces/punctuation with hyphens.","Ensure the first character is a lowercase letter or digit.","Keep the slug to 64 characters or fewer."],"exampleFix":"// before\nvalidateSlug('REST API Notes', 'tag');\n// after\nvalidateSlug('rest-api-notes', 'tag');","handlingStrategy":"validation","validationCode":"const SLUG = /^[a-z0-9][a-z0-9._-]{0,63}$/;\nfunction toSlug(value) {\n  return String(value).toLowerCase().trim().replace(/[^a-z0-9._-]+/g, '-').replace(/^-+/,'').slice(0, 64);\n}\nfunction isValidSlug(value) { return SLUG.test(value); }","typeGuard":"function isSlug(value) {\n  return typeof value === 'string' && /^[a-z0-9][a-z0-9._-]{0,63}$/.test(value);\n}","tryCatchPattern":"try {\n  result = validateSlug(value, label);\n} catch (e) {\n  if (/lowercase letters\\/numbers slug/.test(e.message)) {\n    result = validateSlug(toSlug(value), label);\n  } else throw e;\n}","preventionTips":["Generate slugs from display names with a deterministic slugify function.","Validate slug fields in the form before submission.","Never store display names in slug fields; keep them separate."],"tags":["memory-vault","validation","slug","format"],"backgroundTag":null,"analyzedSha":"01e15490f04e29cfefe3896951f43db46994d8ee","analyzedAt":"2026-08-13T00:31:08.655Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}