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

  1. Wrap the getJupiterPrice() call in try/catch at the call site and degrade the price to null, matching the module's documented contract.
  2. Confirm TOKEN_CONTRACT_ADDRESS in landing/src/lib/constants.ts matches the live Solana mint.
  3. If 429s persist, lengthen TOKEN_STATS_CACHE_MS to reduce request frequency.
  4. 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

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


AI-assisted analysis of jamiepine/voicebox@51f49dea19 (2026-08-12). Data as JSON: /api/errors/888ed4d0d47ce1cb. Report an issue: GitHub.