{"record":{"id":"460f3b3ade40fa18","repo":"koala73/worldmonitor","slug":"invalid-portwatch-seed-metadata","errorCode":null,"errorMessage":"Invalid PortWatch seed metadata","messagePattern":"Invalid PortWatch seed metadata","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/seed-portwatch-port-activity.mjs","lineNumber":1744,"sourceCode":"        canonicalAdvances: false,\n        metaPayload: buildPortActivityFailureMeta(previousMeta, { reason: new Error('SIGTERM') }),\n      });\n    } catch {}\n    try { await releaseLock(LOCK_DOMAIN, runId); } catch {}\n    process.exit(1);\n  };\n  process.on('SIGTERM', onSigterm);\n  process.on('SIGINT', onSigterm);\n\n  try {\n    const prevIso2List = await readSeedSnapshot(CANONICAL_KEY, { strict: true });\n    previousMeta = await readSeedSnapshot(META_KEY, { strict: true });\n    if (prevIso2List !== null && (!Array.isArray(prevIso2List)\n      || prevIso2List.some((iso2) => typeof iso2 !== 'string' || !/^[A-Z]{2}$/.test(iso2)))) {\n      throw new Error('Invalid PortWatch canonical snapshot');\n    }\n    if (previousMeta !== null && (typeof previousMeta !== 'object' || Array.isArray(previousMeta))) {\n      throw new Error('Invalid PortWatch seed metadata');\n    }\n    previousRead = true;\n    prevCountryKeys = Array.isArray(prevIso2List) ? prevIso2List.map(iso2 => `${KEY_PREFIX}${iso2}`) : [];\n\n    console.log(`  Fetching port activity data (60d: last30 + prev30 windows)...`);\n    const {\n      countries,\n      countryData,\n      servedStaleCount,\n      droppedTooOldCount,\n      droppedNoCacheCount,\n      freshFetchedCount,\n      cacheHitCount,\n      retryState,\n      coverage,\n    } = await fetchAll(progress, {\n      signal: shutdownController.signal,\n      expectedCountries: Array.isArray(prevIso2List) ? prevIso2List : [],","sourceCodeStart":1726,"sourceCodeEnd":1762,"githubUrl":"https://github.com/koala73/worldmonitor/blob/7d06c8633d256c18e38133030bc3613976a96ec9/scripts/seed-portwatch-port-activity.mjs#L1726-L1762","documentation":"After loading the canonical snapshot the script reads its seed metadata snapshot (META_KEY) in strict mode. If metadata exists but is not a plain object (e.g. an array, string, or number), the script throws 'Invalid PortWatch seed metadata' rather than run with corrupt bookkeeping data.","triggerScenarios":"readSeedSnapshot(META_KEY, { strict: true }) returns a non-null value that is not a plain object: an array, primitive, or otherwise malformed metadata persisted by a prior run or written out-of-band.","commonSituations":"Metadata overwritten by a tool that wrote a JSON array; partial/corrupt write from an interrupted seed; schema drift where a newer script stores a different metadata shape; manual edits to seed state.","solutions":["Delete the META_KEY snapshot so the seed recreates metadata on the next run","Rewrite META_KEY with the expected plain-object shape (matching the current script's writer)","Check for interrupted prior seed runs and re-run the full seed to regenerate both snapshots","Confirm no other job writes to META_KEY with a different format"],"exampleFix":"// before (invalid array metadata)\n[\"generatedAt\", \"count\"]\n// after\n{ \"generatedAt\": \"2026-09-15T00:00:00Z\", \"count\": 42 }","handlingStrategy":"validation","validationCode":"const meta = await readSeedSnapshot(META_KEY, { strict: false });\nif (meta !== null && (typeof meta !== 'object' || Array.isArray(meta))) {\n  await clearSeedSnapshot(META_KEY);\n}","typeGuard":"const isPlainObject = (v) => v !== null && typeof v === 'object' && !Array.isArray(v);","tryCatchPattern":"try {\n  await runSeed();\n} catch (err) {\n  if (err.message === 'Invalid PortWatch seed metadata') {\n    await resetMetaSnapshot();\n    await runSeed();\n  } else throw err;\n}","preventionTips":["Keep a single writer for META_KEY","Validate metadata shape before persisting","Recover automatically from corrupt metadata by resetting and reseeding"],"tags":["seed","metadata","validation"],"backgroundTag":"schema-validation-failed","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"}