koala73/worldmonitor · error · SeedUnavailableError

${key}

Error message

${key}

What it means

readRequiredSeed reads a seed dataset from the shared Redis-backed JSON cache via readCachedJson(key, true). A seed marked 'required' must be a cache hit AND must decode successfully; otherwise it throws SeedUnavailableError carrying the seed key as its message, signaling the caller (e.g. getHumanitarianSummary) cannot serve data without this pre-seeded dataset.

Solutions

  1. Run/restart the seed worker (or api/bootstrap.js wiring) so the seed for this key is (re)written to Redis; verify seed-meta:<key> exists.
  2. Check Redis credentials and reachability — a cache read error is logged for this key before the throw.
  3. Inspect the cached value with the same key in Redis and fix the decode function if the stored shape drifted.
  4. Wrap the caller to serve a fallback/degraded response when SeedUnavailableError is thrown.

Example fix

// before
const summary = await getHumanitarianSummary(iso3);
// after
let summary;
try {
  summary = await getHumanitarianSummary(iso3);
} catch (err) {
  if (err instanceof SeedUnavailableError) {
    return { summary: null, unavailable: true, seed: err.message };
  }
  throw err;
}
Defensive patterns

Strategy: try-catch

Validate before calling

import { exists } from './seed-check';
// before calling, ensure the seed metadata exists
const seeded = await seedMetaExists(`seed-meta:${key}`); // returns boolean
if (!seeded) {
  console.warn(`Seed ${key} not present; run the seed worker first.`);
}

Type guard

function isSeedUnavailableError(err: unknown): err is SeedUnavailableError {
  return err instanceof SeedUnavailableError;
}

Try / catch

try {
  const data = await readRequiredSeed(key, decode);
} catch (err) {
  if (isSeedUnavailableError(err)) {
    console.error(`Required seed missing or undecodable: ${err.message}`);
    return fallbackData;
  }
  throw err;
}

Prevention

When it happens

Trigger: Requesting a dependent endpoint (e.g. humanitarian summary) when the Redis seed for the key was never written, expired, or evicted; readCachedJson returns status 'miss' or 'error' (Redis down/misconfigured); the cached JSON exists but decode() returns undefined because the stored shape no longer matches the decoder.

Common situations: Fresh environment where seed scripts were never run or UPSTASH credentials are missing; seed TTL expired and the seed worker hasn't refreshed; schema drift after a deploy changed the decoder while old cached JSON remains; Redis outage logged via logCacheReadError before the throw.

Related errors


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

Appendix: source

Thrown at server/_shared/required-seed.ts:18

import { logCacheReadError, readCachedJson } from './redis';

export class SeedUnavailableError extends Error {
  readonly statusCode = 503;

  constructor(key: string) {
    super(`Seed unavailable: ${key}`);
    this.name = 'SeedUnavailableError';
  }
}

export async function readRequiredSeed<T>(
  key: string,
  decode: (value: unknown) => T | undefined,
): Promise<T> {
  const result = await readCachedJson(key, true);
  if (result.status === 'error') logCacheReadError(key, result.error);
  if (result.status !== 'hit') throw new SeedUnavailableError(key);
  const data = decode(result.value);
  if (data === undefined) throw new SeedUnavailableError(key);
  return data;
}

View on GitHub (pinned to e586b8b4b8)