affaan-m/ECC · error · Error
${label} must be an array.
Error message
${label} must be an array. What it means
Thrown by uniqueStrings() when the values argument is not an array. uniqueStrings is the shared validator for list-valued memory fields (tags, links, targetHarnesses, etc.) and requires an actual Array instance.
Source
Thrown at scripts/lib/memory-vault-format.js:117
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}).`);
}
return values.reduce((result, value) => {
const normalized = validator(value);
if (result.includes(normalized)) {
throw new Error(`${label} must not contain duplicate values.`);
}
return [...result, normalized];
}, []);
}
function validateTimestamp(value, label) {
const normalized = asNonEmptyString(value, label, 64);
const parsed = new Date(normalized);
if (
!ISO_TIMESTAMP_PATTERN.test(normalized)View on GitHub (pinned to 01e15490f0)
Solutions
- Wrap the value in an array before passing it (Array.isArray check, or Array.from / [].concat).
- Fix the upstream producer so the field is always a list.
- If the field is optional, pass an empty array rather than null.
Example fix
// before
uniqueStrings('tag-a,tag-b', { label: 'tags', limit: 32, validator: v => v });
// after
uniqueStrings(['tag-a', 'tag-b'], { label: 'tags', limit: 32, validator: v => v }); Defensive patterns
Strategy: type-guard
Validate before calling
function asStringArray(value) {
return Array.isArray(value) ? value : (value == null ? [] : [value]);
}
// before calling uniqueStrings:
const tags = asStringArray(raw.tags); Type guard
function isStringArray(value) {
return Array.isArray(value) && value.every(v => typeof v === 'string');
} Try / catch
try {
result = uniqueStrings(values, opts);
} catch (e) {
if (/must be an array/.test(e.message)) result = uniqueStrings(values == null ? [] : [values], opts);
else throw e;
} Prevention
- Coerce scalar values to single-element arrays at the deserialization boundary.
- Use JSON schema 'type: array' for list fields.
- Default optional list fields to [] rather than null.
When it happens
Trigger: Passing a comma-separated string instead of an array, an object, undefined, or null where the schema expects a list.
Common situations: Deserializing frontmatter that has a scalar where a list was expected; a generator that conditionally produces a single value instead of a list.
Related errors
- ${label} must be a non-empty string.
- ${label} must be a lowercase letters/numbers slug.
- memory id must match mem_<lowercase-id> and cannot contain a
- ${label} has too many values (maximum ${limit}).
- ${label} must not contain duplicate values.
AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13).
Data as JSON: /api/errors/09d7bbcdd9254660.
Report an issue: GitHub.