{"record":{"id":"ed68ed2b5e1b96ae","repo":"koala73/worldmonitor","slug":"rendercountrydevelopments-requires-a-country-name","errorCode":null,"errorMessage":"renderCountryDevelopments requires a country name","messagePattern":"renderCountryDevelopments requires a country name","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/build-crawlable-corpus.mjs","lineNumber":3323,"sourceCode":"    closeList();\n    if (/^NEXT \\d/i.test(trimmed)) {\n      const colonIdx = trimmed.indexOf(':');\n      if (colonIdx !== -1) {\n        const label = applyCrawlableBriefEmphasis(escapeHtml(trimmed.slice(0, colonIdx)));\n        const body = applyCrawlableBriefEmphasis(escapeHtml(trimmed.slice(colonIdx + 1).trim()));\n        out.push(`          <p><strong>${label}:</strong> ${body}</p>`);\n        continue;\n      }\n    }\n    out.push(`          <p>${applyCrawlableBriefEmphasis(escapeHtml(trimmed))}</p>`);\n  }\n  closeList();\n  return out.join('\\n');\n}\n\nexport function renderCountryDevelopments({ countryCode = '', countryName, developments, ciiEntry = null, pulse = null }) {\n  const name = String(countryName || '').trim();\n  if (!name) throw new Error('renderCountryDevelopments requires a country name');\n  const rawRows = developments && typeof developments === 'object' ? developments : null;\n  // Validate the frozen shape before the publish rules run: a malformed brief\n  // must red the build, not be quietly withheld as thin grounding.\n  if (rawRows?.brief && typeof rawRows.brief === 'object') assertDevelopmentsBrief(rawRows.brief);\n  // Publish rules (#7738, #7748): markdown emphasis stripped, model preamble\n  // dropped, the ISO-code heading repaired to the country name, and briefs\n  // grounded on fewer than MIN_BRIEF_GROUNDING_PUBLISHERS withheld. loadCorpusData\n  // already applied them to the committed snapshot; this call is idempotent\n  // so direct callers get the same page.\n  const rows = rawRows\n    ? normalizeFrozenDevelopments(rawRows, { countryCode, countryName: name })\n    : null;\n  const headlines = Array.isArray(rows?.headlines) ? rows.headlines : [];\n  const brief = rows?.brief && typeof rows.brief === 'object' ? rows.brief : null;\n  const timeline = Array.isArray(rows?.timeline) ? rows.timeline : [];\n\n  for (const headline of headlines) assertDevelopmentsHeadline(headline);\n  if (brief) assertDevelopmentsBrief(brief);","sourceCodeStart":3305,"sourceCodeEnd":3341,"githubUrl":"https://github.com/koala73/worldmonitor/blob/7d06c8633d256c18e38133030bc3613976a96ec9/scripts/build-crawlable-corpus.mjs#L3305-L3341","documentation":"build-crawlable-corpus.mjs exports renderCountryDevelopments(), which renders a country's frozen recent-developments section into HTML for the crawlable corpus. It derives a display name from countryName, trims it, and throws immediately if the trimmed result is empty, because a country page without a name cannot produce a valid ISO-heading-repaired section. The build fails loudly rather than publish a malformed or anonymous country brief.","triggerScenarios":"Calling renderCountryDevelopments({ countryCode, countryName, developments, ... }) with countryName undefined, null, an empty string, or a whitespace-only string (or a value that coerces to '' such as an empty object/array). countryCode defaults to '' but countryName does not, so omitting the property entirely also triggers this.","commonSituations":"Build-time config/data mistakes: a country metadata lookup returns an object missing `name`; a data file field was renamed from `name` to `label`; a generator passes through sparse rows for countries with partial data; a refactor switched callers to pass only countryCode, assuming the renderer derives the name from the code.","solutions":["Pass a non-empty countryName string: renderCountryDevelopments({ countryCode, countryName: country.name, developments })","Fix the upstream data source so every country record has a populated `name` field before the build consumes it","If only the ISO code is available, resolve the code to a display name (country metadata map) before calling the renderer","Add a per-record check that fails fast with a message naming the offending country record"],"exampleFix":"// before\nconst html = renderCountryDevelopments({ countryCode: 'de', developments });\n// after\nconst countryName = COUNTRY_NAMES['de'];\nif (!countryName?.trim()) throw new Error(`no name for country de`);\nconst html = renderCountryDevelopments({ countryCode: 'de', countryName, developments });","handlingStrategy":"validation","validationCode":"export function assertValidCountryDevelopmentsInput({ countryName, developments }) {\n  if (typeof countryName !== 'string' || !countryName.trim()) {\n    throw new Error(`countryName must be a non-empty string, got ${JSON.stringify(countryName)}`);\n  }\n  if (developments !== null && typeof developments !== 'object') {\n    throw new Error('developments must be an object or null');\n  }\n}","typeGuard":"const hasCountryName = (c) =>\n  typeof c?.countryName === 'string' && c.countryName.trim().length > 0;","tryCatchPattern":"try {\n  html = renderCountryDevelopments({ countryCode, countryName, developments });\n} catch (err) {\n  if (err.message.includes('requires a country name')) {\n    throw new Error(`country record ${countryCode} is missing a display name`);\n  }\n  throw err;\n}","preventionTips":["Validate country metadata (name present and non-empty) when loading data files, before rendering pages","Derive countryName from a single shared country-metadata map so all callers get consistent values","Add a build-time linter that flags country records with empty name fields","Use a schema validator (e.g. zod) on renderer inputs at module boundaries"],"tags":["build","validation","missing-argument"],"backgroundTag":"empty-required-field","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"}