koala73/worldmonitor · error · Error

cache_all_null

Error message

cache_all_null

What it means

Thrown inside the country-markets MCP tool path when every Redis data read (overview, categories, movers, etc. for a given country_code) returned null or undefined. This is a contract-parity guard matching the cache-tool executeTool path: if ALL data keys are null the response is degenerate (Redis transient, stampede, or pre-seed), so it throws a plain Error to surface as a tool-execution failure in dispatchToolsCall. For Pro callers the already-reserved billing slot stays charged because the tool did execute.

Source

Thrown at api/mcp/registry/rpc-tools.ts:1224

        { key: `seed-meta:consumer-prices:categories:${code}:30d`, maxStaleMin: 1500 },
        { key: `seed-meta:consumer-prices:movers:${code}:30d`,     maxStaleMin: 1500 },
        { key: `seed-meta:consumer-prices:spread:${code}`,         maxStaleMin: 1500 }, // producer's actual key shape
        { key: `seed-meta:consumer-prices:freshness:${code}`,      maxStaleMin: 1500 },
      ];

      const [dataResults, metaResults] = await Promise.all([
        Promise.all(dataKeys.map((k) => readJsonFromUpstash(k))),
        Promise.all(freshnessChecks.map((c) => readJsonFromUpstash(c.key))),
      ]);

      // F6 contract parity with the cache-tool path (executeTool, ~line 1139):
      // if every data read is null/undefined, this is a degenerate-empty
      // response (Redis transient / stampede / pre-seed). Throw so
      // dispatchToolsCall reports a normal tool-execution failure. For Pro
      // callers the already-reserved slot stays charged because the tool has
      // executed.
      if (dataResults.every((v: unknown) => v === null || v === undefined)) {
        throw new Error('cache_all_null');
      }

      const { cached_at, stale } = evaluateFreshness(freshnessChecks, metaResults);

      return {
        cached_at,
        stale,
        country_code: code,
        data: {
          overview: dataResults[0],
          categories: dataResults[1],
          movers: dataResults[2],
          retailerSpread: dataResults[3],
          freshness: dataResults[4],
        },
      };
    },
    // Hybrid tool covers the consumer-prices domain via direct Redis reads

View on GitHub (pinned to ffec79ac33)

Solutions

  1. Verify the market data keys exist in Redis for that country_code — check GET /api/health for the relevant market seed-meta entries.
  2. Retry after the market data cadence — stampede and transient null conditions resolve on TTL refresh.
  3. Try a different country_code known to be seeded (e.g. 'US') to confirm whether the issue is country-specific or systemic.
  4. If systemic, verify the market seeder (Yahoo Finance / provider) is running and the data keys are being written.
Defensive patterns

Strategy: retry

Validate before calling

// Verify market data is seeded for the country before calling
const health = await fetch('/api/health').then(r => r.json());
const marketKeys = health.coverage ? Object.keys(health.coverage).filter(k => k.includes(`market:${countryCode}`)) : [];
if (marketKeys.length === 0) {
  throw new Error(`No market data seeded for ${countryCode}; try a different country or wait for seeder`);
}

Type guard

function isCacheAllNullError(e: unknown): boolean {
  return e instanceof Error && e.message === 'cache_all_null';
}

Try / catch

try {
  const snapshot = await callMcpTool('get_market_snapshot', { country_code: 'US' });
} catch (e) {
  if (e instanceof Error && e.message === 'cache_all_null') {
    // Degenerate empty — retry after seeder cycle, or try a known-seeded country
    await backoffRetry(30_000);
  } else throw e;
}

Prevention

When it happens

Trigger: Calling the country markets snapshot tool for a country_code where all market data keys are null in Redis — before the market seeder has populated that country, after a Redis flush, or during a stampede where all concurrent cold reads see null. The freshness metadata reads may still succeed (seed-meta keys exist), but the data keys themselves are absent.

Common situations: A country_code that has never been seeded (the market seeder covers a specific symbol set); a Redis flush or eviction that dropped the data keys but left seed-meta keys; a weekend/holiday where the market data producer did not run and TTLs expired.

Related errors


AI-assisted analysis of koala73/worldmonitor@ffec79ac33 (2026-08-12). Data as JSON: /api/errors/ae9161d16590d4ba. Report an issue: GitHub.