koala73/worldmonitor · error · Error
${PRO_HTML_PATH} must carry exactly one dateModified node (f
Error message
${PRO_HTML_PATH} must carry exactly one dateModified node (found ${dates.length}) What it means
scripts/build-welcome-teasers.mjs updates the JSON-LD `dateModified` field in the PRO welcome HTML so its schema.org metadata matches the capture date. The build asserts the file contains exactly one `"dateModified": "YYYY-MM-DD"` node; if the regex finds zero or multiple occurrences, renderProHtml throws rather than silently rewriting the wrong (or no) node. This guards against a stale or duplicated structured-data block corrupting SEO metadata.
Solutions
- Open the PRO HTML file and grep for `"dateModified":` — ensure exactly one occurrence in the exact form `"dateModified": "YYYY-MM-DD"`
- If zero matches, restore/fix the JSON-LD dateModified node to the exact format the regex expects
- If multiple matches, remove the duplicate JSON-LD block or extra dateModified fields, keeping one
- If the file intentionally uses a different date format, update both the match/replace regex in scripts/build-welcome-teasers.mjs and the file consistently
Example fix
// before (duplicated/stale JSON-LD in pro html) "dateModified": "2024-01-01" ... "dateModified": "2023-11-05" // after "dateModified": "2024-01-01"
Defensive patterns
Strategy: validation
Validate before calling
const dates = html.match(/"dateModified": "\d{4}-\d{2}-\d{2}"/g) || [];
if (dates.length !== 1) throw new Error(`expected exactly 1 dateModified node, found ${dates.length}`); Type guard
const hasSingleDateModified = (html) => (html.match(/"dateModified": "\d{4}-\d{2}-\d{2}"/g) || []).length === 1; Try / catch
try {
const html = renderProHtml({ capturedAt });
} catch (e) {
if (e.message.includes('exactly one dateModified')) fixJsonLdInTemplate();
else throw e;
} Prevention
- Keep the JSON-LD dateModified in the exact `"dateModified": "YYYY-MM-DD"` format
- Never duplicate the JSON-LD structured-data block when editing the template
- Add a lint/grep CI check asserting exactly one dateModified occurrence
- Run the teaser build after any welcome-HTML template change
When it happens
Trigger: Calling renderProHtml (or running the script as main) when public PRO_HTML_PATH file contains 0 dateModified nodes (field renamed, quoting style changed to single quotes or no space after colon) or 2+ nodes (duplicate JSON-LD blocks added, template concatenation duplicated the field).
Common situations: A developer edits the welcome HTML template and reformats the JSON-LD (e.g. drops the space after the colon, so the strict regex no longer matches → 0 found); a templating step injects the JSON-LD block twice; merging branches duplicates the metadata node.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- needs a summary for the llms.txt Comparisons section
- ${relativePath} is missing required live-pulse sections
- ${pagePath} heading leaks ISO code: ${text}
- ${pagePath} brief heading leaks an ISO-3166 alpha-2 code
- no initial JS assets referenced by ${dashboardPath} — run: $
AI-assisted analysis of koala73/worldmonitor@7d06c8633d (2026-09-15).
Data as JSON: /api/errors/36cd3c98d1be3024.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/build-welcome-teasers.mjs:264
const html = readFileSync(htmlPath, 'utf8');
const lastmodMatches = html.match(/<meta name="lastmod" content="\d{4}-\d{2}-\d{2}" \/>/g) || [];
const dateModifiedMatches = html.match(/"dateModified": "\d{4}-\d{2}-\d{2}"/g) || [];
if (lastmodMatches.length !== 1 || dateModifiedMatches.length !== 1) {
throw new Error(
`${WELCOME_HTML_PATH} must carry exactly one lastmod meta and one dateModified node `
+ `(found ${lastmodMatches.length} / ${dateModifiedMatches.length}) — refusing to guess which one tracks the strip snapshot`,
);
}
return html
.replace(/<meta name="lastmod" content="\d{4}-\d{2}-\d{2}" \/>/, `<meta name="lastmod" content="${capturedAt}" />`)
.replace(/"dateModified": "\d{4}-\d{2}-\d{2}"/, `"dateModified": "${capturedAt}"`);
}
export function renderProHtml({ rootDir = REPO_ROOT, capturedAt } = {}) {
const html = readFileSync(join(rootDir, PRO_HTML_PATH), 'utf8');
const dates = html.match(/"dateModified": "\d{4}-\d{2}-\d{2}"/g) || [];
if (dates.length !== 1) {
throw new Error(`${PRO_HTML_PATH} must carry exactly one dateModified node (found ${dates.length})`);
}
return html.replace(/"dateModified": "\d{4}-\d{2}-\d{2}"/, `"dateModified": "${capturedAt}"`);
}
const isMain = process.argv[1] && resolve(process.argv[1]) === __filename;
if (isMain) {
const check = process.argv.includes('--check');
const snapshotPath = resolveLatestLivePulseSnapshotPath(REPO_ROOT);
const snapshot = JSON.parse(readFileSync(join(REPO_ROOT, snapshotPath), 'utf8'));
const teasers = buildWelcomeTeasers(snapshot, snapshotPath);
const expectedTeasers = `${JSON.stringify({ _comment: comment(snapshotPath, snapshot.capturedAt), ...teasers }, null, 2)}\n`;
const expectedHtml = renderWelcomeHtml({ rootDir: REPO_ROOT, capturedAt: teasers.capturedAt });
const expectedProHtml = renderProHtml({ rootDir: REPO_ROOT, capturedAt: teasers.capturedAt });
const outPath = join(REPO_ROOT, TEASERS_OUTPUT_PATH);
const htmlPath = join(REPO_ROOT, WELCOME_HTML_PATH);
const proHtmlPath = join(REPO_ROOT, PRO_HTML_PATH);
if (check) {
const stale = [];View on GitHub (pinned to 7d06c8633d)