koala73/worldmonitor · error · Error
renderCountryDevelopments requires a country name
Error message
renderCountryDevelopments requires a country name
What it means
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.
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
Example fix
// before
const html = renderCountryDevelopments({ countryCode: 'de', developments });
// after
const countryName = COUNTRY_NAMES['de'];
if (!countryName?.trim()) throw new Error(`no name for country de`);
const html = renderCountryDevelopments({ countryCode: 'de', countryName, developments }); Defensive patterns
Strategy: validation
Validate before calling
export function assertValidCountryDevelopmentsInput({ countryName, developments }) {
if (typeof countryName !== 'string' || !countryName.trim()) {
throw new Error(`countryName must be a non-empty string, got ${JSON.stringify(countryName)}`);
}
if (developments !== null && typeof developments !== 'object') {
throw new Error('developments must be an object or null');
}
} Type guard
const hasCountryName = (c) => typeof c?.countryName === 'string' && c.countryName.trim().length > 0;
Try / catch
try {
html = renderCountryDevelopments({ countryCode, countryName, developments });
} catch (err) {
if (err.message.includes('requires a country name')) {
throw new Error(`country record ${countryCode} is missing a display name`);
}
throw err;
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.
Related errors
- Comparison measurement requires the captured snapshot date
- Report slug/id must match ${SLUG_PATTERN}: ${report.slug} /
- Report ${report.id}: ${role} ${id} has no valid route in the
- ${label} HTTP 400
- Could not resolve ${JSON.stringify(echoCountryInput(raw))} t
AI-assisted analysis of koala73/worldmonitor@7d06c8633d (2026-09-15).
Data as JSON: /api/errors/ed68ed2b5e1b96ae.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/build-crawlable-corpus.mjs:3323
closeList();
if (/^NEXT \d/i.test(trimmed)) {
const colonIdx = trimmed.indexOf(':');
if (colonIdx !== -1) {
const label = applyCrawlableBriefEmphasis(escapeHtml(trimmed.slice(0, colonIdx)));
const body = applyCrawlableBriefEmphasis(escapeHtml(trimmed.slice(colonIdx + 1).trim()));
out.push(` <p><strong>${label}:</strong> ${body}</p>`);
continue;
}
}
out.push(` <p>${applyCrawlableBriefEmphasis(escapeHtml(trimmed))}</p>`);
}
closeList();
return out.join('\n');
}
export function renderCountryDevelopments({ countryCode = '', countryName, developments, ciiEntry = null, pulse = null }) {
const name = String(countryName || '').trim();
if (!name) throw new Error('renderCountryDevelopments requires a country name');
const rawRows = developments && typeof developments === 'object' ? developments : null;
// Validate the frozen shape before the publish rules run: a malformed brief
// must red the build, not be quietly withheld as thin grounding.
if (rawRows?.brief && typeof rawRows.brief === 'object') assertDevelopmentsBrief(rawRows.brief);
// Publish rules (#7738, #7748): markdown emphasis stripped, model preamble
// dropped, the ISO-code heading repaired to the country name, and briefs
// grounded on fewer than MIN_BRIEF_GROUNDING_PUBLISHERS withheld. loadCorpusData
// already applied them to the committed snapshot; this call is idempotent
// so direct callers get the same page.
const rows = rawRows
? normalizeFrozenDevelopments(rawRows, { countryCode, countryName: name })
: null;
const headlines = Array.isArray(rows?.headlines) ? rows.headlines : [];
const brief = rows?.brief && typeof rows.brief === 'object' ? rows.brief : null;
const timeline = Array.isArray(rows?.timeline) ? rows.timeline : [];
for (const headline of headlines) assertDevelopmentsHeadline(headline);
if (brief) assertDevelopmentsBrief(brief);View on GitHub (pinned to 7d06c8633d)