koala73/worldmonitor · error

JODI Gas catalog has no valid publication

Error message

JODI Gas catalog has no valid publication

What it means

resolveGasArchiveUrl() validates the JODI Gas catalog JSON before using it: it requires a positive safe-integer publicationId and an array files. If either is missing or of the wrong type/shape, it throws 'JODI Gas catalog has no valid publication'. This detects upstream catalog format changes or partial responses before any URL is constructed.

Solutions

  1. Inspect the raw catalog JSON from CATALOG_URL and compare against the expected shape (publicationId as positive integer, files array)
  2. Update resolveGasArchiveUrl() and seed-jodi-gas.mjs if the JODI catalog schema changed
  3. Re-run after confirming the endpoint returns the genuine catalog (no proxy/error JSON)
  4. Check seed-meta/cache for a stale catalog copy being validated

Example fix

// before
throw new Error('JODI Gas catalog has no valid publication');
// after (surface what actually came back)
throw new Error(`JODI Gas catalog has no valid publication: ${JSON.stringify(catalog).slice(0, 200)}`);
Defensive patterns

Strategy: type-guard

Validate before calling

if (!Number.isSafeInteger(catalog?.publicationId) || catalog.publicationId <= 0 || !Array.isArray(catalog.files)) {
  throw new Error('catalog shape invalid before calling resolveGasArchiveUrl');
}

Type guard

function hasValidPublication(catalog) {
  return Number.isSafeInteger(catalog?.publicationId) && catalog.publicationId > 0 && Array.isArray(catalog?.files);
}

Try / catch

try {
  const zipUrl = resolveGasArchiveUrl(catalog);
} catch (err) {
  if (err.message.includes('no valid publication')) console.error('Catalog JSON shape changed; inspect the raw payload');
  throw err;
}

Prevention

When it happens

Trigger: The catalog endpoint returns JSON without publicationId (schema change), with a string or float id, or with files absent/null; a proxy or error page returns JSON that isn't the real catalog.

Common situations: JODI publishes a new catalog schema, an intermediate/proxy replaces the response, or a cached/stale catalog snapshot is validated.

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


AI-assisted analysis of koala73/worldmonitor@e586b8b4b8 (2026-09-22). Data as JSON: /api/errors/ed4f57cf49615ae0. Report an issue: GitHub.

Appendix: source

Thrown at scripts/seed-jodi-gas.mjs:267

    if (!filename || entryName === filename || entryName.endsWith('/' + filename)) {
      if ((flags & 0x08) && compSize === 0) {
        throw new Error(`JODI Gas ZIP: entry uses data-descriptor (bit 3) — compSize unknown in local header`);
      }
      return { dataOffset, compSize, compression, entryName };
    }
    // If bit 3 is set, compSize in local header is 0 — fall back to byte scan
    if ((flags & 0x08) && compSize === 0) { offset++; continue; }
    offset = dataOffset + compSize;
  }
  return null;
}

// Publication IDs identify specific releases. Follow the same catalog as the
// official download page; a successful fetch of an old ID never finds new data.
function resolveGasArchiveUrl(catalog) {
  if (!Number.isSafeInteger(catalog?.publicationId) || catalog.publicationId <= 0
    || !Array.isArray(catalog.files)) {
    throw new Error('JODI Gas catalog has no valid publication');
  }
  const csvFiles = catalog.files.filter(file => file?.format === 'CSV' && file.ignore === false);
  if (csvFiles.length !== 1 || csvFiles[0].filename !== 'GAS_world_NewFormat.zip') {
    throw new Error('JODI Gas catalog must contain one supported CSV archive');
  }
  return `${ARCHIVE_ROOT}/${catalog.publicationId}/${csvFiles[0].filename}`;
}

async function fetchAndParseCsv() {
  // Discovery and archive download share the existing total request budget.
  const signal = AbortSignal.timeout(120_000);
  const catalogResponse = await fetch(CATALOG_URL, {
    headers: { 'User-Agent': CHROME_UA },
    signal,
    redirect: 'error',
  });
  if (!catalogResponse.ok) throw new Error(`JODI Gas catalog fetch failed: HTTP ${catalogResponse.status}`);
  const zipUrl = resolveGasArchiveUrl(await catalogResponse.json());

View on GitHub (pinned to e586b8b4b8)