koala73/worldmonitor · error · Error
Incomplete PortWatch coverage; canonical retained
Error message
Incomplete PortWatch coverage; canonical retained
What it means
At the end of a seed run the script compares achieved country coverage against the target. If the canonical state cannot advance (coverage.refreshFailures non-empty and usable countries below the target), full publication is blocked and the script throws 'Incomplete PortWatch coverage; canonical retained' so previously published canonical data is preserved instead of being replaced with partial data.
Solutions
- Re-run the seed after the upstream/API failures resolve; canonical state is retained so it is safe to retry
- Inspect coverage.refreshFailures in the run log to identify which countries failed and why
- Verify API credentials and rate limits for the port-activity upstream source
- Reduce target coverage or add retry/backoff for flaky countries only if partial publication is acceptable policy-wise
- Check Redis connectivity and write permissions for the seed worker
Defensive patterns
Strategy: retry
Try / catch
try {
await runSeed();
} catch (err) {
if (err.message === 'Incomplete PortWatch coverage; canonical retained') {
const delayMs = 5 * 60 * 1000;
await scheduleRetry(delayMs);
} else throw err;
} Prevention
- Add per-country retry with backoff for upstream fetches
- Monitor upstream port-activity API health before scheduling seeds
- Alert on coverage.refreshFailures so failures are fixed before the next run
When it happens
Trigger: The run's canonicalAdvances flag is false: one or more country refreshes failed (network errors, upstream PortWatch/API outages, empty responses), leaving countryData.size below coverage.target, and the script reaches the final guard before logSeedResult.
Common situations: Upstream maritime/port API outage or rate limiting during the 60d window fetch; Redis write failures for some countries; running the seed with a broken API key causing per-country fetch failures; transient network partition in Railway worker.
Related errors
- Wikidata request failed with status ${response.status}
- relay returned ${resp.status}
- failed to build coverage snapshot
- COMPANY_MONITORING_ADMISSION_EVIDENCE_STALE
- COMPANY_MONITORING_CLASSIFICATION_REPLAY_CONFLICT
AI-assisted analysis of koala73/worldmonitor@7d06c8633d (2026-09-15).
Data as JSON: /api/errors/19698f56e2f7a2a2.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/seed-portwatch-port-activity.mjs:1812
metaPayload,
canonicalAdvances,
});
failureRecorded = !canonicalAdvances;
let canonicalState = 'no prior canonical list';
if (canonicalAdvances) {
canonicalState = `canonical list advanced to ${countries.length} countries`;
} else if (prevIso2List !== null) {
canonicalState = canonicalTtlExtended
? `canonical list retained at ${prevIso2List.length} countries`
: `canonical retention unconfirmed (${prevIso2List.length} countries at run start)`;
}
console.log(
` ${canonicalAdvances ? 'State saved' : 'Recovery state saved'}; ${canonicalState}; ` +
`usable coverage ${countryData.size}/${coverage.target}; ` +
`${canonicalAdvances ? '' : 'full publication blocked; '}` +
`${coverage.refreshFailures.length} unresolved refresh failures`,
);
if (!canonicalAdvances) throw new Error('Incomplete PortWatch coverage; canonical retained');
logSeedResult('supply_chain', countryData.size, Date.now() - startedAt, { source: 'portwatch-ports' });
console.log(` Seeded ${countryData.size} countries`);
console.log(`\n=== Done (${Date.now() - startedAt}ms) ===`);
} catch (err) {
console.error(` SEED FAILED: ${err.message}`);
await extendExistingTtl([CANONICAL_KEY, META_KEY, ...prevCountryKeys], TTL).catch(() => {});
if (previousRead && !failureRecorded) {
await publishPortActivitySnapshot({
countryData: new Map(),
canonicalAdvances: false,
metaPayload: buildPortActivityFailureMeta(previousMeta, { reason: err }),
});
}
throw err;
} finally {
process.removeListener('SIGTERM', onSigterm);
process.removeListener('SIGINT', onSigterm);View on GitHub (pinned to 7d06c8633d)