jamiepine/voicebox · warning · Error
Jupiter HTTP ${res.status}
Error message
Jupiter HTTP ${res.status} What it means
Thrown by getJupiterPrice() when Jupiter's lite price API (lite-api.jup.ag/price/v3) returns a non-2xx status while fetching the USD price for the $VOICEBOX mint. It is a server-side Next.js fetch with ISR revalidation (REVALIDATE_S = TOKEN_STATS_CACHE_MS/1000). The token-stats module's header comment declares 'Never throws' — this throw must be isolated by the caller so a price failure degrades only the price metric to null and lands in `warnings`.
Source
Thrown at landing/src/lib/token-stats.ts:390
capped = true;
break;
}
}
const top = [...balances.entries()]
.sort((a, b) => b[1] - a[1])
.slice(0, TOP_HOLDERS)
.map(([owner, raw]) => ({ owner, amount: raw / 10 ** decimals }));
return { count: balances.size, capped, top };
}
// ── Price (Jupiter, no key required) ─────────────────────────────────────────
async function getJupiterPrice(mint: string): Promise<number | null> {
const res = await fetch(`https://lite-api.jup.ag/price/v3?ids=${mint}`, {
next: { revalidate: REVALIDATE_S },
});
if (!res.ok) throw new Error(`Jupiter HTTP ${res.status}`);
const json = (await res.json()) as Record<string, { usdPrice?: number } | undefined>;
const price = json[mint]?.usdPrice;
return typeof price === 'number' ? price : null;
}
// ── Creator rewards (pump.fun swap-api) ──────────────────────────────────────
// Lifetime creator fees earned by the creator wallet, in SOL. The swap-api
// returns a daily series with a running `cumulativeCreatorFeeSOL`; the latest
// (max) bucket is the lifetime total. The per-coin endpoint is unreliable, so
// we use the per-creator one.
interface CreatorFeeBucket {
cumulativeCreatorFeeSOL: string;
}
async function getCreatorRewardsSol(): Promise<number | null> {
const res = await fetch(
`https://swap-api.pump.fun/v1/creators/${TOKEN_CREATOR_ADDRESS}/fees?interval=1d`,
{ next: { revalidate: REVALIDATE_S }, headers: { 'User-Agent': 'voicebox.sh' } },
);View on GitHub (pinned to 51f49dea19)
Solutions
- Wrap the getJupiterPrice() call in try/catch at the call site and degrade the price to null, matching the module's documented contract.
- Confirm TOKEN_CONTRACT_ADDRESS in landing/src/lib/constants.ts matches the live Solana mint.
- If 429s persist, lengthen TOKEN_STATS_CACHE_MS to reduce request frequency.
- Add one retry with backoff for transient 5xx before degrading to null.
Example fix
// before
const price = await getJupiterPrice(MINT);
// after
let price: number | null = null;
try {
price = await getJupiterPrice(MINT);
} catch (e) {
warnings.push(`price: ${(e as Error).message}`);
} Defensive patterns
Strategy: fallback
Validate before calling
function isRevalidateFresh(lastFetchMs: number, cacheMs: number): boolean {
return Date.now() - lastFetchMs < cacheMs;
}
// Only call getJupiterPrice when the cache is stale, reducing 429 risk. Type guard
function isJupiterPricePayload(
json: unknown,
mint: string,
): json is Record<string, { usdPrice?: number } | undefined> {
return typeof json === 'object' && json !== null && mint in json;
} Try / catch
let price: number | null = null;
try {
price = await getJupiterPrice(MINT);
} catch (e) {
warnings.push(`price: ${(e as Error).message}`);
price = null; // degrade one metric, keep rendering
} Prevention
- Honor the module's 'never throws' contract: every sub-fetch is wrapped and degrades a single metric to null.
- Tune REVALIDATE_S against the upstream rate limit; longer cache = fewer hits.
- Log failures into `warnings` so degraded metrics are visible, not silent.
When it happens
Trigger: GET https://lite-api.jup.ag/price/v3?ids=<TOKEN_CONTRACT_ADDRESS> responds 429 (rate limit), 404/400 (unknown or gapped mint), or 5xx (Jupiter outage). Any DNS/TCP failure before the status check surfaces as a fetch rejection rather than this message.
Common situations: Jupiter lite-api is unauthenticated and rate-limits aggressively under load; a viral token page being crawled can trip 429s. A freshly deployed or migrated mint may 404 until Jupiter indexes it. Stale REVALIDATE_S values that are too short amplify rate-limit pressure.
Related errors
- pump.fun swap-api HTTP ${res.status}
- Failed to fetch model info: ${response.status}
- Failed to fetch releases
- releases ${r.status}
- Failed to fetch releases
AI-assisted analysis of jamiepine/voicebox@51f49dea19 (2026-08-12).
Data as JSON: /api/errors/888ed4d0d47ce1cb.
Report an issue: GitHub.