koala73/worldmonitor · error · Error

formatCrawlableIntelBrief requires a country name

Error message

formatCrawlableIntelBrief requires a country name

What it means

formatCrawlableIntelBrief(text, countryName) converts a stored markdown-ish LLM intel brief into prerendered HTML for country pages. It requires a non-empty trimmed countryName because it rewrites the brief's 'means for' section title using the page's country name — stored briefs may still contain ISO codes from the TIER1-only prompt fallback (issue #7738). When countryName is missing, empty, or whitespace-only the function throws rather than emitting HTML with a blank or wrong title.

Solutions

  1. Pass the resolved country name as the second argument, e.g. formatCrawlableIntelBrief(brief.text, country.displayName).
  2. Guard before calling: if the country record has no non-blank name, skip and log instead of formatting.
  3. If upstream data can legitimately lack a name, decide on an explicit fallback (e.g. ISO code) upstream — never pass an empty string to satisfy the check.
  4. Grep all call sites of formatCrawlableIntelBrief after refactors to ensure none pass a renamed or missing field.

Example fix

// before
const html = formatCrawlableIntelBrief(brief.text, row.country_nm);
// after
const name = country?.displayName ?? row.name;
if (!name || !name.trim()) throw new Error(`no country name for ${row.code}`);
const html = formatCrawlableIntelBrief(brief.text, name);
Defensive patterns

Strategy: type-guard

Validate before calling

if (!countryName || !String(countryName).trim()) {
  throw new Error(`formatCrawlableIntelBrief: missing countryName for brief`);
}

Type guard

const hasCountryName = (v) => typeof v === 'string' && v.trim().length > 0;

Try / catch

let html;
try {
  html = formatCrawlableIntelBrief(brief.text, country.displayName);
} catch (err) {
  if (err.message === 'formatCrawlableIntelBrief requires a country name') {
    console.warn(`Skipping brief for ${country?.code}: missing display name`);
    return null;
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling formatCrawlableIntelBrief(briefText, countryName) with countryName undefined, null, '', or any value whose String() coercion trims to empty — typically during country-page generation when the country record failed to load or a refactored field name passes undefined.

Common situations: Generating crawlable country pages from cached/stored data where the display-name field is absent or empty for some records; a refactor renamed the data key so undefined is passed; iterating a partial country list; calling the formatter standalone (e.g. in a test) with only the text argument.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


AI-assisted analysis of koala73/worldmonitor@7d06c8633d (2026-09-15). Data as JSON: /api/errors/174004b82c9a446f. Report an issue: GitHub.

Appendix: source

Thrown at scripts/build-crawlable-corpus.mjs:3269

      .trim();
    if (next === current) break;
    current = next;
  }
  return current;
}

function applyCrawlableBriefEmphasis(escaped) {
  return escaped.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>').replace(/\*\*/g, '');
}

// Frozen intel briefs are markdown-ish LLM text. Country pages are prerendered
// HTML for crawlers, so convert emphasis and promote the five section titles
// rather than injecting the string into a <p>. Always rewrite the "means for"
// title from the page's country name: stored briefs still contain ISO codes
// from the TIER1-only prompt fallback (#7738).
export function formatCrawlableIntelBrief(text, countryName) {
  const name = String(countryName || '').trim();
  if (!name) throw new Error('formatCrawlableIntelBrief requires a country name');
  const out = [];
  let listOpen = false;
  const closeList = () => {
    if (listOpen) {
      out.push('          </ul>');
      listOpen = false;
    }
  };
  const openList = () => {
    if (!listOpen) {
      out.push('          <ul>');
      listOpen = true;
    }
  };

  for (const rawLine of String(text || '').split('\n')) {
    const trimmed = unwrapBriefEmphasisLine(rawLine.trim());
    if (!trimmed) {

View on GitHub (pinned to 7d06c8633d)