koala73/worldmonitor · error

failed to build coverage snapshot

Error message

failed to build coverage snapshot

What it means

The Fastify route GET /worldmonitor/coverage (query market, default 'ae') returned 500 because buildCoverageSnapshot(market) threw. The builder aggregates coverage SQL through the pg pool in src/db/client.ts, so in practice the throw is almost always database-side: DATABASE_URL unset (getPool throws 'DATABASE_URL is not set'), a connection failure (5s connect timeout), or a SQL/shape error. The route logs the real cause via fastify.log.error and returns only this generic string.

Source

Thrown at consumer-prices-core/src/api/routes/worldmonitor.ts:20

import { buildCoverageSnapshot } from '../../snapshots/coverage.js';
import {
  buildBasketSeriesSnapshot,
  buildCategoriesSnapshot,
  buildFreshnessSnapshot,
  buildMoversSnapshot,
  buildOverviewSnapshot,
  buildRetailerSpreadSnapshot,
} from '../../snapshots/worldmonitor.js';

export async function worldmonitorRoutes(fastify: FastifyInstance) {
  fastify.get('/coverage', async (request, reply) => {
    const { market = 'ae' } = request.query as { market?: string };
    try {
      const data = await buildCoverageSnapshot(market);
      return reply.send(data);
    } catch (err) {
      fastify.log.error(err);
      return reply.status(500).send({ error: 'failed to build coverage snapshot' });
    }
  });

  fastify.get('/overview', async (request, reply) => {
    const { market = 'ae' } = request.query as { market?: string };
    try {
      const data = await buildOverviewSnapshot(market);
      return reply.send(data);
    } catch (err) {
      fastify.log.error(err);
      return reply.status(500).send({ error: 'failed to build overview snapshot' });
    }
  });

  fastify.get('/movers', async (request, reply) => {
    const { market = 'ae', days = '30' } = request.query as { market?: string; days?: string };
    try {
      const data = await buildMoversSnapshot(market, parseInt(days, 10));

View on GitHub (pinned to eeab0a219f)

Solutions

  1. Check the server logs — fastify.log.error printed the underlying cause immediately before the 500
  2. Verify DATABASE_URL is set in the API process and points at a reachable Postgres
  3. If the cause is SQL/shape, reproduce by calling buildCoverageSnapshot(market) directly with the same market
  4. If pool exhaustion, raise the pg Pool max or batch snapshot callers

Example fix

// before — boot the API without checking the snapshot dependency
await app.listen({ port: 3000 });

// after — fail fast when the database backing the route is missing
if (!process.env.DATABASE_URL) throw new Error('DATABASE_URL is not set');
await query('SELECT 1');
await app.listen({ port: 3000 });
Defensive patterns

Strategy: retry

Validate before calling

// startup preflight for every worldmonitor snapshot route
async function snapshotDepsReady(): Promise<boolean> {
  if (!process.env.DATABASE_URL) return false;
  try { await query('SELECT 1'); return true; } catch { return false; }
}

Try / catch

let data;
for (let attempt = 1; attempt <= 3; attempt++) {
  const res = await fetch('/worldmonitor/coverage?market=ae');
  if (res.ok) { data = await res.json(); break; }
  if (res.status !== 500) throw new Error(`coverage: HTTP ${res.status}`);
  await sleep(attempt * 1_000); // db-backed 500s are usually transient
}

Prevention

When it happens

Trigger: API process started without DATABASE_URL; Postgres unreachable or credentials wrong (pool connect timeout 5000ms); a market value hitting a query path that throws; a SQL regression in buildCoverageSnapshot; pool exhaustion past max 10 connections.

Common situations: Deployed API missing its database secret; local run against a stopped Postgres; a migration renaming a column the snapshot query selects; load pushing past the 10-connection pool so queries queue and time out.

Understand the failure class

Background: Database query failed: Internal Server Error 500s wrapping SQL, Prisma, and connection failures — what to check first — this error's family across 16 libraries.

Related errors


AI-assisted analysis of koala73/worldmonitor@eeab0a219f (2026-08-21). Data as JSON: /api/errors/29018eeec510f265. Report an issue: GitHub.