koala73/worldmonitor · critical · Error
Redis not configured
Error message
Redis not configured
What it means
Thrown at the top of the health snapshot read path when getRedisCredentials() returns null, meaning at least one of UPSTASH_REDIS_REST_URL or UPSTASH_REDIS_REST_TOKEN is unset. The health endpoint treats Redis as a hard dependency (it can assess nothing without it), so this surfaces as a 503 REDIS_DOWN response that UptimeRobot/k8s probes read as a hard failure. It fires before any Redis command is attempted.
Source
Thrown at api/health.js:2624
const lastFailureRaw = results?.[0]?.result;
const failureLogRaw = results?.[1]?.result;
const body = {
lastFailure: parseJson(lastFailureRaw),
failureLog: Array.isArray(failureLogRaw)
? failureLogRaw.map(parseJson).filter((e) => e !== null)
: [],
checkedAt: new Date().toISOString(),
};
return new Response(JSON.stringify(body, null, 2), { status: 200, headers });
}
// A snapshot hit is one Redis command instead of the ~390-command registry
// sweep below. A failed snapshot read is a real Redis outage, not a cache
// miss: returning 503 preserves UptimeRobot's hard-down signal.
let refreshLockToken = null;
let ownsSnapshotRefreshLock = false;
try {
if (!getRedisCredentials()) throw new Error('Redis not configured');
// Read the snapshot this request will actually render. `?compact=1` — the
// browser poll, ~115k/day — reads the ~1 KB compact key instead of dragging the
// full ~20 KB check map out of Redis to show a tenth of it (#5300).
const snapshotKey = compact ? HEALTH_VERDICT_COMPACT_SNAPSHOT_KEY : HEALTH_VERDICT_SNAPSHOT_KEY;
const snapshotResult = await redisPipeline([['GET', snapshotKey]], 4_000);
if (!snapshotResult) throw new Error('Redis request failed');
if (snapshotResult[0]?.error) throw new Error('Redis snapshot read failed');
const cachedSnapshot = parseHealthVerdictSnapshot(snapshotResult[0]?.result, snapshotNow(), { requireChecks: !compact });
// Activation deadlines are exact to the second, so the 60s verdict cache
// must not outlive either rollout grace. A snapshot written just before a
// deadline would otherwise keep serving a softened verdict for up to a
// minute after strictness was supposed to begin. Sweep fresh instead.
if (cachedSnapshot && !hasExpiredActivationGrace(cachedSnapshot, snapshotNow())) {
return healthResponse(cachedSnapshot, compact, headers);
}
refreshLockToken = `${now}:${crypto.randomUUID()}`;
let lockResult = await redisPipeline([[View on GitHub (pinned to ffec79ac33)
Solutions
- Set UPSTASH_REDIS_REST_URL and UPSTASH_REDIS_REST_TOKEN in the Vercel project environment (or .env.local for local dev) and redeploy.
- Run `npm run worktree:env` (or worktree:bootstrap) in a fresh worktree to link the ignored .env.local that carries the Upstash credentials.
- Verify with `vercel env ls` (or the Vercel dashboard) that both keys exist for the target environment (Production/Preview/Development).
- Confirm the credentials point at a reachable Upstash REST endpoint by issuing one pipeline GET out-of-band.
Example fix
// before (env missing) // UPSTASH_REDIS_REST_URL and UPSTASH_REDIS_REST_TOKEN unset // // after (in .env.local / Vercel project env) UPSTASH_REDIS_REST_URL=https://your-db.upstash.io UPSTASH_REDIS_REST_TOKEN=AX...token
Defensive patterns
Strategy: validation
Validate before calling
import { getRedisCredentials } from './_upstash-json.js';
function assertRedisConfigured() {
if (!getRedisCredentials()) {
throw new Error('Missing UPSTASH_REDIS_REST_URL or UPSTASH_REDIS_REST_TOKEN');
}
}
// Call at the start of any code path that depends on Redis. Type guard
function redisConfigured(): boolean {
return Boolean(process.env.UPSTASH_REDIS_REST_URL && process.env.UPSTASH_REDIS_REST_TOKEN);
} Try / catch
try {
if (!getRedisCredentials()) throw new Error('Redis not configured');
// ... snapshot read
} catch (err) {
// health.js already maps this to a 503 REDIS_DOWN; callers of redisPipeline
// should distinguish missing-config (env fix) from outage (retry).
if (err.message === 'Redis not configured') {
// surface as a deployment-config incident, not a transient retry
}
} Prevention
- Add a startup self-check in non-production environments that asserts UPSTASH_REDIS_REST_URL and UPSTASH_REDIS_REST_TOKEN are set.
- Keep the Upstash env vars in the Vercel project (all environments: Production, Preview, Development) and verify with `vercel env ls` after rotations.
- Run `npm run worktree:env` in fresh worktrees so the ignored .env.local is linked.
- Treat 'Redis not configured' as a config error distinct from runtime outage — do not retry, fix the env.
When it happens
Trigger: A GET /api/health (or ?compact=1) request hits api/health.js:2624 on a Vercel Edge deployment where the Upstash Redis env vars were never set, were renamed, or were stripped from the preview/branch environment. Also triggered locally without a .env.local carrying the Upstash credentials.
Common situations: New preview/branch deploys that did not inherit the production Upstash env vars; a renamed or rotated env var after a secret rotation; running the endpoint in a fresh worktree that was not bootstrapped with `npm run worktree:env`; misconfigured Railway/Docker relay missing the same vars.
Related errors
- Redis request failed
- Redis snapshot lock failed
- Redis snapshot wait failed
- Redis snapshot lock retry failed
- No event feeds are available for exposure enrichment
AI-assisted analysis of koala73/worldmonitor@ffec79ac33 (2026-08-12).
Data as JSON: /api/errors/c308448b6aa604fe.
Report an issue: GitHub.