koala73/worldmonitor · error
failed to build categories snapshot
Error message
failed to build categories snapshot
What it means
The Fastify route GET /worldmonitor/categories returned 500 because buildCategoriesSnapshot(market, range) threw. Inputs are market (default 'ae') and range (default '30d'); the builder computes per-category WoW/MoM deltas and sparklines via the pg pool in src/db/client.ts. The 500 wraps a database-side throw — missing DATABASE_URL, connection failure, SQL regression — or a range value the date-bucketing logic cannot handle.
Source
Thrown at consumer-prices-core/src/api/routes/worldmonitor.ts:78
fastify.get('/freshness', async (request, reply) => {
const { market = 'ae' } = request.query as { market?: string };
try {
const data = await buildFreshnessSnapshot(market);
return reply.send(data);
} catch (err) {
fastify.log.error(err);
return reply.status(500).send({ error: 'failed to build freshness snapshot' });
}
});
fastify.get('/categories', async (request, reply) => {
const { market = 'ae', range = '30d' } = request.query as { market?: string; range?: string };
try {
const data = await buildCategoriesSnapshot(market, range);
return reply.send(data);
} catch (err) {
fastify.log.error(err);
return reply.status(500).send({ error: 'failed to build categories snapshot' });
}
});
fastify.get('/basket-series', async (request, reply) => {
const { market = 'ae', basket = 'essentials-ae', range = '30d' } = request.query as {
market?: string;
basket?: string;
range?: string;
};
try {
const data = await buildBasketSeriesSnapshot(market, basket, range);
return reply.send(data);
} catch (err) {
fastify.log.error(err);
return reply.status(500).send({ error: 'failed to build basket series snapshot' });
}
});
}View on GitHub (pinned to eeab0a219f)
Solutions
- Check the fastify error log for the underlying exception
- Retry with the default range ('30d') to determine whether the range value or the database is at fault
- Verify DATABASE_URL and database reachability
- Reproduce with buildCategoriesSnapshot(market, range) directly
Defensive patterns
Strategy: retry
Validate before calling
const VALID_RANGES = new Set(['7d', '30d', '90d']);
const range = params.get('range') ?? '30d';
if (!VALID_RANGES.has(range)) {
throw new Error(`range '${range}' unsupported — use one of ${[...VALID_RANGES].join(', ')}`);
}
const res = await fetch(`/worldmonitor/categories?market=ae&range=${range}`); Try / catch
const res = await fetch(`/worldmonitor/categories?market=${market}&range=${range}`);
if (res.status === 500) {
// first rule out a bad range by retrying the default
const fallbackRes = await fetch(`/worldmonitor/categories?market=${market}&range=30d`);
if (fallbackRes.ok) return await fallbackRes.json();
throw new Error('categories snapshot backend down');
}
if (!res.ok) throw new Error(`categories: HTTP ${res.status}`); Prevention
- Constrain range values to a published set instead of forwarding arbitrary strings into date math
- Confirm category stats exist for newly onboarded markets before exposing them
- Treat simultaneous 500s across snapshot routes as a database incident, not per-route bugs
When it happens
Trigger: DATABASE_URL unset; Postgres unreachable; a range string like '7d'/'90d' hitting unsupported date math in the builder; a migration breaking the category aggregate; pool exhaustion.
Common situations: Client sending an ad-hoc range value the builder never anticipated; deployment missing the DB secret; category stats absent for a newly onboarded market.
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
- failed to build coverage snapshot
- failed to build overview snapshot
- failed to build movers snapshot
- failed to build retailer spread snapshot
- failed to build freshness snapshot
AI-assisted analysis of koala73/worldmonitor@eeab0a219f (2026-08-21).
Data as JSON: /api/errors/07f5c52b3624f6e3.
Report an issue: GitHub.