koala73/worldmonitor · error · Error

Report ${report.id}: ${role} ${id} has no valid route in the

Error message

Report ${report.id}: ${role} ${id} has no valid route in the chokepoint registry

What it means

After slug validation, the research-reports builder resolves each referenced chokepoint (focus and context) through the chokepoint registry to build its route. If a referenced chokepoint id has no entry whose slug passes SLUG_PATTERN, the link would be dead, so the build throws identifying the report, role, and id.

Solutions

  1. Fix the report's focusChokepointId / contextChokepointIds to reference an id that exists in the chokepoint registry.
  2. If the chokepoint was intentionally removed, remove or replace the reference in the report data.
  3. Regenerate/rebuild the chokepoint registry so chokepointSlugById is current before the reports build.
  4. Consider a data-level referential-integrity check so bad ids fail at authoring time.

Example fix

// before
{ id: 'hormuz-strait', contextChokepointIds: ['suez-canal-old'] }
// after
{ id: 'hormuz-strait', contextChokepointIds: ['suez-canal'] }
Defensive patterns

Strategy: validation

Validate before calling

const missing = [report.focusChokepointId, ...report.contextChokepointIds]
  .filter((id) => !chokepointSlugById.has(id));
if (missing.length) throw new Error(`unknown chokepoint ids: ${missing.join(', ')}`);

Try / catch

try { renderReport(report); } catch (e) { console.error(e.message); /* fix registry or report refs */ }

Prevention

When it happens

Trigger: A report's focusChokepointId or an entry in contextChokepointIds references an id absent from chokepointSlugById (typo, deleted chokepoint, registry not yet regenerated) when the reports build runs.

Common situations: Renaming/removing a chokepoint in the registry without updating reports that reference it; typos in report front-matter ids; registry built from a different data revision than the reports.

Understand the failure class

Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.

Related errors


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

Appendix: source

Thrown at scripts/build-research-reports.mjs:718

  tpl,
  baseUrl,
  lastmod,
  chokepointSlugById,
  dataCatalog,
  includedInDataCatalog,
}) {
  const { escapeHtml, absoluteUrl, breadcrumbLd, withUtmSource, pageDocument } = tpl;
  const SLUG_PATTERN = /^[a-z0-9][a-z0-9-]*$/;
  if (!SLUG_PATTERN.test(report.slug) || !SLUG_PATTERN.test(report.id)) {
    throw new Error(`Report slug/id must match ${SLUG_PATTERN}: ${report.slug} / ${report.id}`);
  }
  for (const [role, ids] of [
    ['focusChokepointId', [report.focusChokepointId]],
    ['contextChokepointId', report.contextChokepointIds],
  ]) {
    for (const id of ids) {
      if (!SLUG_PATTERN.test(chokepointSlugById.get(id) ?? '')) {
        throw new Error(`Report ${report.id}: ${role} ${id} has no valid route in the chokepoint registry`);
      }
    }
  }
  const chokepointSlug = chokepointSlugById.get(report.focusChokepointId);
  const path = `/research/${report.slug}/`;
  const canonical = absoluteUrl(baseUrl, path);
  const focus = snapshot.chokepoints[report.focusChokepointId];
  const resolve = (text) => resolveMetricTokens(escapeHtml(text), metrics, escapeHtml);
  const m = (id) => {
    const metric = metrics.get(id);
    if (!metric) throw new Error(`Unknown metric: ${id}`);
    return metric;
  };
  // Plain-text description for surfaces that cannot carry <data> markup; the
  // definition writes it with {{m:...}} tokens so its numbers cannot drift
  // from the computed metrics.
  const description = resolvePlainMetricTokens(report.description, metrics);
  const partialMonth = focus.observationEnd.slice(0, 7);

View on GitHub (pinned to 7d06c8633d)