{"record":{"id":"2aab21cf0c12be9a","repo":"koala73/worldmonitor","slug":"chokepoint-id-field-must-be-an-array","errorCode":null,"errorMessage":"${chokepoint.id}: ${field} must be an array","messagePattern":"(.+?): (.+?) must be an array","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/build-crawlable-corpus.mjs","lineNumber":1382,"sourceCode":"  const countryByCode = new Map(countries.map((country) => [country.code, country]));\n  const crisisBySlug = new Map(crises.map((crisis) => [crisis.slug, crisis]));\n  const byChokepointId = new Map();\n  const byCountryCode = new Map();\n  const byCrisisSlug = new Map();\n  for (const chokepoint of chokepoints) {\n    const declaration = content[chokepoint.id] || {};\n    const editorialLinks = declaration.editorialLinks ?? [];\n    if (!Array.isArray(editorialLinks)) throw new Error(`${chokepoint.id}: editorialLinks must be an array`);\n    const editorial = new Map();\n    for (const link of editorialLinks) {\n      if (!blogPostPaths.has(link?.href) || typeof link.label !== 'string' || !link.label.trim()) {\n        throw new Error(`${chokepoint.id}: editorialLinks requires a canonical blog post path and label: ${link?.href}`);\n      }\n      if (!editorial.has(link.href)) editorial.set(link.href, link);\n    }\n    const resolve = (field, targets, inverse) => {\n      const ids = declaration[field] ?? [];\n      if (!Array.isArray(ids)) throw new Error(`${chokepoint.id}: ${field} must be an array`);\n      return [...new Set(ids)].map((id) => {\n        const target = targets.get(id);\n        if (!target) throw new Error(`${chokepoint.id}: ${field} contains unknown target ${id}`);\n        const entries = inverse.get(id) || [];\n        entries.push(chokepoint);\n        inverse.set(id, entries);\n        return target;\n      });\n    };\n    byChokepointId.set(chokepoint.id, {\n      countries: resolve('countryCodes', countryByCode, byCountryCode),\n      crises: resolve('crisisSlugs', crisisBySlug, byCrisisSlug),\n      editorial: [...editorial.values()],\n    });\n  }\n  return { byChokepointId, byCountryCode, byCrisisSlug };\n}\n","sourceCodeStart":1364,"sourceCodeEnd":1400,"githubUrl":"https://github.com/koala73/worldmonitor/blob/7d06c8633d256c18e38133030bc3613976a96ec9/scripts/build-crawlable-corpus.mjs#L1364-L1400","documentation":"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.","triggerScenarios":"content[chokepoint.id].countryCodes or content[chokepoint.id].crisisSlugs is a non-nullish non-array value (string, object, number) when buildChokepointPageLinks() iterates that chokepoint.","commonSituations":"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.","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."],"exampleFix":"// before\ncountryCodes: 'YE'\n// after\ncountryCodes: ['YE']","handlingStrategy":"validation","validationCode":"for (const field of ['countryCodes', 'crisisSlugs']) {\n  const v = content[chokepoint.id]?.[field];\n  if (v != null && !Array.isArray(v)) throw new TypeError(`${chokepoint.id}: ${field} must be an array`);\n}","typeGuard":"const isIdArray = (v) => v == null || (Array.isArray(v) && v.every((id) => typeof id === 'string'));","tryCatchPattern":null,"preventionTips":["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."],"tags":["build-time","schema-validation","type-mismatch"],"backgroundTag":"type-mismatch","analyzedSha":"7d06c8633d256c18e38133030bc3613976a96ec9","analyzedAt":"2026-09-15T16:44:39.439Z","contentChangedAt":"2026-09-15T16:44:39.439Z","schemaVersion":2},"datasetVersion":"2026-09-15T18:17:12.389Z"}