{"record":{"id":"64ac3fbee2ce8171","repo":"vercel-labs/agent-skills","slug":"daily-quota-exceeded-64ac3f","errorCode":"DAILY_QUOTA_EXCEEDED","errorMessage":"DAILY_QUOTA_EXCEEDED","messagePattern":"DAILY_QUOTA_EXCEEDED","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"skills/vercel-optimize/lib/vercel.mjs","lineNumber":337,"sourceCode":"    stdout = err.stdout || '';\n    stderr = err.stderr || '';\n    exitCode = err.code ?? err.exitCode ?? 1;\n  }\n  const safeStderr = redactSensitiveText(stderr);\n\n  if (stdout && stdout.trim().startsWith('{')) {\n    try {\n      const data = JSON.parse(stdout);\n      if (data && typeof data === 'object' && data.error) {\n        const failure = {\n          ok: false,\n          code: data.error.code || `EXIT_${exitCode}`,\n          message: redactSensitiveText(data.error.message || ''),\n          allowedValues: data.error.allowedValues,\n          stderr: safeStderr,\n        };\n        return isDailyQuotaExceeded(failure)\n          ? { ...failure, code: 'DAILY_QUOTA_EXCEEDED', originalCode: failure.code }\n          : failure;\n      }\n      if (exitCode === 0) return { ok: true, data };\n      // Exit non-zero, no `error` key, parseable stdout → still useful.\n      return { ok: true, data };\n    } catch {\n      /* fall through to stderr categorization */\n    }\n  }\n\n  // Metrics schema returns a top-level array.\n  if (stdout && stdout.trim().startsWith('[')) {\n    try {\n      const data = JSON.parse(stdout);\n      if (exitCode === 0) return { ok: true, data };\n    } catch { /* fall through */ }\n  }\n","sourceCodeStart":319,"sourceCodeEnd":355,"githubUrl":"https://github.com/vercel-labs/agent-skills/blob/b8caa260a420a73042e35521de4b5c8baf6446cc/skills/vercel-optimize/lib/vercel.mjs#L319-L355","documentation":"DAILY_QUOTA_EXCEEDED is a structured failure returned (not thrown) by runVercelJson when a Vercel CLI command prints JSON whose embedded error signals that the Vercel Observability Plus daily query allowance has been spent. The library normalizes any matching upstream code or message to this canonical code and preserves the original code in originalCode. It exists because vercel metrics / vercel usage run against a per-team, per-UTC-day Observability query budget, and once exhausted every further metrics call fails identically until midnight UTC.","triggerScenarios":"Any call routed through runVercelJson that hits the Observability Plus API surface — getMetricsSchema (vercel metrics schema --format json), per-route metric timeseries (vercel metrics), or usage queries (vercel usage) — after the team's daily query quota is depleted. It fires when the parsed stdout error.code is DAILY_QUOTA_EXCEEDED OR when the combined message/stderr/detail matches /daily.*observability.*query limit/i (isDailyQuotaExceeded in throttle.mjs:224). CategorizeError also re-derives it from a bare stderr match when stdout is not JSON.","commonSituations":"Running vercel-optimize repeatedly in one day across many routes or long time windows; large fan-out analysis (every route × 14-day window) that burns hundreds of queries; a shared team token consumed by teammates' concurrent CLI sessions; a CI job that re-runs the full report hourly. Also triggered simply by having a low Observability Plus tier on a high-traffic project, or by forgetting the throttle caches the block so retries never succeed.","solutions":["Wait for the UTC-midnight reset — the result.cachedUntil timestamp gives the exact unblock time; stop retrying metrics calls until then. The throttle (getMetricThrottle) already short-circuits via getDailyQuotaBlock, so no code change is needed to stop the bleed.","Narrow query scope to stay under quota: fewer routes, shorter time windows, or skip per-route detail and use aggregate metrics only for the remainder of the day.","Lower sustained pressure with env tuning: VERCEL_OPTIMIZE_METRIC_CONCURRENCY (default 8) and VERCEL_OPTIMIZE_METRIC_RATE (default 80/60s) so the day's budget is amortized instead of spent in the first burst.","Cache metric output to disk on first successful run and reuse it for reruns instead of re-querying the Observability API.","Move the run to a team/account with a higher Observability Plus tier, or upgrade the tier to raise the daily query cap."],"exampleFix":"// before — fans out per-route queries until quota dies\nfor (const route of allRoutes) {\n  const r = await getMetricThrottle().run(() => runVercelJson(['metrics', route, '--range', '14d']));\n  if (!r.ok) throw new Error(r.code);\n}\n\n// after — respect the cached block, narrow scope, cache to disk\nconst block = getDailyQuotaBlock();\nif (block) {\n  console.error(`Daily Observability quota spent until ${block.cachedUntil}; skipping metrics.`);\n} else {\n  const routes = priorityRoutes; // fewer, targeted\n  const r = await getMetricThrottle().run(() => runVercelJson(['metrics', '--range', '7d'], { routes }));\n  if (r.ok) await fs.writeFile('metrics-cache.json', JSON.stringify(r.data));\n  else if (isDailyQuotaExceeded(r)) console.error(`Quota exceeded; resumes ${r.cachedUntil}`);\n  else throw new Error(r.code);\n}","handlingStrategy":"type-guard","validationCode":"import { getDailyQuotaBlock } from './lib/throttle.mjs';\n\n// Run BEFORE issuing any vercel metrics / usage call to avoid a wasted request.\nconst block = getDailyQuotaBlock();\nif (block) {\n  // Quota already known exhausted; do not call the API.\n  console.warn(`Skipping metrics: daily quota blocked until ${new Date(block.untilMs).toISOString()}`);\n  return null;\n}\nconst result = await getMetricThrottle().run(() => runVercelJson(['metrics', '--format', 'json']));","typeGuard":"import { isDailyQuotaExceeded } from './lib/throttle.mjs';\n\n/** Narrows a runVercelJson result to the daily-quota-exceeded failure shape. */\nfunction isDailyQuotaFailure(r) {\n  return Boolean(r && r.ok === false && r.code === 'DAILY_QUOTA_EXCEEDED');\n}\n\n// Usage:\nconst r = await runVercelJson(['metrics', '--format', 'json']);\nif (isDailyQuotaFailure(r)) {\n  // r.code === 'DAILY_QUOTA_EXCEEDED', r.originalCode has the upstream code,\n  // r.cachedUntil is the ISO reset time. Do NOT retry; schedule for after midnight UTC.\n}\n// isDailyQuotaExceeded(r) also catches stderr/message variants pre-normalization.","tryCatchPattern":null,"preventionTips":["Treat the result as data: check r.ok and narrow on r.code === 'DAILY_QUOTA_EXCEEDED' (plus isDailyQuotaExceeded for the raw-stderr variant) — it is returned, never thrown, so a try/catch will not catch it.","Call getDailyQuotaBlock() before each metric batch and bail out early; the throttle caches the block until UTC midnight so re-issuing queries is pure waste.","Cap fan-out: pass a bounded route list and shorter --range windows rather than 'all routes × 14d', which is the fastest way to spend the daily budget.","Set VERCEL_OPTIMIZE_METRIC_CONCURRENCY and VERCEL_OPTIMIZE_METRIC_RATE below their defaults when sharing a team token so concurrent sessions do not collectively exhaust quota.","Persist successful metric output to disk and reuse it across same-day reruns; only re-query when the cache is missing or stale past midnight UTC.","Read result.cachedUntil and originalCode on this failure — originalCode tells you the real upstream error and cachedUntil tells the exact moment retries become productive."],"tags":["vercel","observability","quota","rate-limit","metrics","api","cli"],"backgroundTag":null,"analyzedSha":"b8caa260a420a73042e35521de4b5c8baf6446cc","analyzedAt":"2026-08-13T06:03:29.508Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}