koala73/worldmonitor · error · Error
${chokepoint.id}: ${field} must be an array
Error message
${chokepoint.id}: ${field} must be an array What it means
Inside buildChokepointPageLinks(), the resolve() helper reads declaration[field] for 'countryCodes' and 'crisisSlugs' and requires an array of ids. This throw fires when one of those relation fields is present but not an array (e.g. a single string code). Like the other checks in this function it is a fail-fast build-time validation that stops malformed chokepoint relation data from reaching generated pages.
Solutions
- Change the field in the chokepoint declaration to an array of string ids, e.g. countryCodes: ['US','IR'] or crisisSlugs: ['red-sea-crisis'].
- If the value arrives as a delimited string from an external source, coerce before the call: Array.isArray(v) ? v : String(v).split(',').
- Add a schema validation step over content declarations so wrong shapes surface before the build.
Example fix
// before countryCodes: 'YE' // after countryCodes: ['YE']
Defensive patterns
Strategy: validation
Validate before calling
for (const field of ['countryCodes', 'crisisSlugs']) {
const v = content[chokepoint.id]?.[field];
if (v != null && !Array.isArray(v)) throw new TypeError(`${chokepoint.id}: ${field} must be an array`);
} Type guard
const isIdArray = (v) => v == null || (Array.isArray(v) && v.every((id) => typeof id === 'string'));
Prevention
- Coerce single scalar ids into arrays at the data-source boundary.
- Validate externally loaded declarations with a schema (zod/ajv).
- Normalize loader output with a to-array helper before the build runs.
- Keep declarations in reviewed, validated files rather than ad-hoc edits.
When it happens
Trigger: content[chokepoint.id].countryCodes or content[chokepoint.id].crisisSlugs is a non-nullish non-array value (string, object, number) when buildChokepointPageLinks() iterates that chokepoint.
Common situations: Author writes countryCodes: 'YE' instead of ['YE']; a YAML/JSON loader returns a comma-separated string; a merge tool collapses a single-element array into a scalar; an external data export delivers the wrong JSON type.
Understand the failure class
Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.
Related errors
- ${chokepoint.id}: editorialLinks must be an array
- Exa search failed HTTP ${resp.status}: ${text.slice(0, 120)}
- ECCC_MALFORMED_PAGE
- /accuracy/ meta description must be 155–160 chars (got ${len
- writeAccuracySection requires the canonical DataCatalog iden
AI-assisted analysis of koala73/worldmonitor@7d06c8633d (2026-09-15).
Data as JSON: /api/errors/2aab21cf0c12be9a.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/build-crawlable-corpus.mjs:1382
const countryByCode = new Map(countries.map((country) => [country.code, country]));
const crisisBySlug = new Map(crises.map((crisis) => [crisis.slug, crisis]));
const byChokepointId = new Map();
const byCountryCode = new Map();
const byCrisisSlug = new Map();
for (const chokepoint of chokepoints) {
const declaration = content[chokepoint.id] || {};
const editorialLinks = declaration.editorialLinks ?? [];
if (!Array.isArray(editorialLinks)) throw new Error(`${chokepoint.id}: editorialLinks must be an array`);
const editorial = new Map();
for (const link of editorialLinks) {
if (!blogPostPaths.has(link?.href) || typeof link.label !== 'string' || !link.label.trim()) {
throw new Error(`${chokepoint.id}: editorialLinks requires a canonical blog post path and label: ${link?.href}`);
}
if (!editorial.has(link.href)) editorial.set(link.href, link);
}
const resolve = (field, targets, inverse) => {
const ids = declaration[field] ?? [];
if (!Array.isArray(ids)) throw new Error(`${chokepoint.id}: ${field} must be an array`);
return [...new Set(ids)].map((id) => {
const target = targets.get(id);
if (!target) throw new Error(`${chokepoint.id}: ${field} contains unknown target ${id}`);
const entries = inverse.get(id) || [];
entries.push(chokepoint);
inverse.set(id, entries);
return target;
});
};
byChokepointId.set(chokepoint.id, {
countries: resolve('countryCodes', countryByCode, byCountryCode),
crises: resolve('crisisSlugs', crisisBySlug, byCrisisSlug),
editorial: [...editorial.values()],
});
}
return { byChokepointId, byCountryCode, byCrisisSlug };
}
View on GitHub (pinned to 7d06c8633d)