{"record":{"id":"19698f56e2f7a2a2","repo":"koala73/worldmonitor","slug":"incomplete-portwatch-coverage-canonical-retained","errorCode":null,"errorMessage":"Incomplete PortWatch coverage; canonical retained","messagePattern":"Incomplete PortWatch coverage; canonical retained","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/seed-portwatch-port-activity.mjs","lineNumber":1812,"sourceCode":"      metaPayload,\n      canonicalAdvances,\n    });\n    failureRecorded = !canonicalAdvances;\n    let canonicalState = 'no prior canonical list';\n    if (canonicalAdvances) {\n      canonicalState = `canonical list advanced to ${countries.length} countries`;\n    } else if (prevIso2List !== null) {\n      canonicalState = canonicalTtlExtended\n        ? `canonical list retained at ${prevIso2List.length} countries`\n        : `canonical retention unconfirmed (${prevIso2List.length} countries at run start)`;\n    }\n    console.log(\n      `  ${canonicalAdvances ? 'State saved' : 'Recovery state saved'}; ${canonicalState}; ` +\n      `usable coverage ${countryData.size}/${coverage.target}; ` +\n      `${canonicalAdvances ? '' : 'full publication blocked; '}` +\n      `${coverage.refreshFailures.length} unresolved refresh failures`,\n    );\n    if (!canonicalAdvances) throw new Error('Incomplete PortWatch coverage; canonical retained');\n\n    logSeedResult('supply_chain', countryData.size, Date.now() - startedAt, { source: 'portwatch-ports' });\n    console.log(`  Seeded ${countryData.size} countries`);\n    console.log(`\\n=== Done (${Date.now() - startedAt}ms) ===`);\n  } catch (err) {\n    console.error(`  SEED FAILED: ${err.message}`);\n    await extendExistingTtl([CANONICAL_KEY, META_KEY, ...prevCountryKeys], TTL).catch(() => {});\n    if (previousRead && !failureRecorded) {\n      await publishPortActivitySnapshot({\n        countryData: new Map(),\n        canonicalAdvances: false,\n        metaPayload: buildPortActivityFailureMeta(previousMeta, { reason: err }),\n      });\n    }\n    throw err;\n  } finally {\n    process.removeListener('SIGTERM', onSigterm);\n    process.removeListener('SIGINT', onSigterm);","sourceCodeStart":1794,"sourceCodeEnd":1830,"githubUrl":"https://github.com/koala73/worldmonitor/blob/7d06c8633d256c18e38133030bc3613976a96ec9/scripts/seed-portwatch-port-activity.mjs#L1794-L1830","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  await runSeed();\n} catch (err) {\n  if (err.message === 'Incomplete PortWatch coverage; canonical retained') {\n    const delayMs = 5 * 60 * 1000;\n    await scheduleRetry(delayMs);\n  } else throw err;\n}","preventionTips":["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"],"tags":["seed","coverage","upstream","retry"],"backgroundTag":"upstream-api-error","analyzedSha":"7d06c8633d256c18e38133030bc3613976a96ec9","analyzedAt":"2026-09-15T16:44:39.439Z","contentChangedAt":"2026-09-15T16:44:39.439Z","schemaVersion":2},"datasetVersion":"2026-09-15T18:17:12.389Z"}