koala73/worldmonitor · error

is missing following prose

Error message

 is missing following prose

What it means

renderHeadingSection renders an H2 plus its paragraphs for a comparison page. A heading with no following body text produces an empty section, which is both an SEO/content quality problem and an invalid page shape, so the build throws '<label|heading> is missing following prose' when renderParagraphs returns nothing for a heading.

Solutions

  1. Populate the prose array paired with that heading in the page seed (check the field name matches what applyComparisonNarrative merges, e.g. switchProse for switchHeading).
  2. If the section is intentionally absent, remove the heading too — a heading without prose is what triggers the throw.
  3. Re-run the build and confirm the section renders with at least one <p>.

Example fix

// before
switchHeading: 'When to switch tools',
switchProse: [],
// after
switchHeading: 'When to switch tools',
switchProse: [
  'Switch to World Monitor when you need sub-daily updates across ...',
],
Defensive patterns

Strategy: validation

Validate before calling

for (const [heading, prose] of [['evaluation', page.evaluationProse], ['switch', page.switchProse]]) {
  if (heading && !(Array.isArray(prose) && prose.length)) throw new Error(`${page.slug}: ${heading} heading has no prose`);
}

Type guard

const hasHeadingProse = (p) => typeof p === 'object' && p !== null && Array.isArray(p.prose) && p.prose.length > 0;

Prevention

When it happens

Trigger: renderHeadingSection called with a truthy heading whose paragraphs argument is undefined, null, [], or an array of empty strings, causing body.length === 0.

Common situations: A seed defines a heading (e.g. evaluationHeading, switchHeading) but the corresponding prose array key is misspelled or missing; content editors emptied the paragraph array; a refactor renamed the prose field but not the heading field.

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


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

Appendix: source

Thrown at scripts/build-comparison-pages.mjs:495

    throw new Error(page.slug + ' is missing the switch-trigger section');
  }
  if (!merged.methodologyProse?.length) {
    throw new Error(page.slug + ' is missing methodology prose');
  }
  return merged;
}

export const COMPARISON_PAGES = COMPARISON_PAGE_SEEDS.map(applyComparisonNarrative);

function renderParagraphs(paragraphs, escapeHtml) {
  return (paragraphs ?? []).map((paragraph) => '      <p>' + escapeHtml(paragraph) + '</p>');
}

function renderHeadingSection(heading, paragraphs, escapeHtml, label) {
  if (!heading) return [];
  const body = renderParagraphs(paragraphs, escapeHtml);
  if (body.length === 0) {
    throw new Error((label || heading) + ' is missing following prose');
  }
  return ['      <h2>' + escapeHtml(heading) + '</h2>', ...body];
}

function renderKeywordSection(page, escapeHtml) {
  const headingProse = page.headingProse ?? [];
  const profiles = page.competitorProfiles ?? [];
  const profileBlocks = profiles.flatMap((profile) => {
    if (!profile.paragraphs?.length) {
      throw new Error(page.slug + ' profile "' + profile.name + '" has no prose');
    }
    const tag = page.heading ? 'h3' : 'h2';
    return [
      '      <' + tag + '>' + escapeHtml(profile.name) + '</' + tag + '>',
      ...renderParagraphs(profile.paragraphs, escapeHtml),
    ];
  });
  if (page.heading) {

View on GitHub (pinned to 7d06c8633d)