vercel/next.js · error · Error
Could not fetch upgrade metadata. Please try again.
Error message
Could not fetch upgrade metadata. Please try again.
What it means
fetchJSON wraps every failure of an HTTP fetch (network errors, timeouts from the 10s AbortSignal, redirects, non-2xx statuses, invalid JSON bodies) into a single generic error with the original error attached as `cause`. The message is intentionally generic because the underlying cause (in error.cause) carries the detail.
Solutions
- Read error.cause to find the real failure (HTTP status, timeout, DNS error) and address it.
- Retry the command — the message itself says 'Please try again' and failures may be transient rate limits.
- Check network/proxy access to registry.npmjs.org and api.github.com from the environment.
- If rate limited by GitHub, wait for the rate-limit window to reset or use authenticated access.
- Set NEXT_TELEMETRY_DISABLED and verify no proxy env vars (HTTP_PROXY/HTTPS_PROXY) interfere.
Example fix
// before
const { value } = await fetchJSON(url) // generic wrapper error
// after
try {
const { value } = await fetchJSON(url)
} catch (error) {
console.error('cause:', (error as Error).cause) // e.g. Error: HTTP 403
} Defensive patterns
Strategy: retry
Validate before calling
// preflight connectivity before calling upgrade APIs
async function canReach(url: string) {
try {
const res = await fetch(url, { method: 'HEAD', signal: AbortSignal.timeout(5000) })
return res.ok || res.status < 500
} catch { return false }
}
await Promise.all([canReach('https://registry.npmjs.org/next/latest'), canReach('https://api.github.com/advisories?per_page=1')]) Type guard
function hasCause(error: unknown): error is Error & { cause: unknown } {
return error instanceof Error && 'cause' in error
} Try / catch
try {
const prep = await prepareUpgrade(dir, 'latest')
} catch (error) {
const cause = (error as Error).cause
if (String(cause).includes('HTTP 403') || String(cause).includes('HTTP 429')) {
// rate limited — back off and retry later
} else if (String(cause).includes('TimeoutError')) {
// network slow — retry with better connectivity
}
throw error
} Prevention
- Run upgrades on a machine with verified access to registry.npmjs.org and api.github.com
- Always inspect error.cause — the top-level message is intentionally generic
- Add retry with backoff around prepareUpgrade in CI
- Watch for GitHub API rate limits (unauthenticated: 60 req/h per IP)
When it happens
Trigger: Any fetchJSON call in prepare-upgrade.ts (npm registry next/latest, registry document, GitHub advisories API, npm bulk advisory endpoint) when: the network is down, the 10s timeout fires, the server returns a non-ok status (rethrown as `HTTP <status>` then wrapped), a redirect occurs (redirect: 'error'), or the response body is not valid JSON.
Common situations: Offline or firewalled CI environments; GitHub API rate limiting (HTTP 403/429); corporate proxies blocking api.github.com; npm registry outages; responses blocked by a proxy returning HTML instead of JSON.
Understand the failure class
Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.
Related errors
- Could not check for security updates. Please try again.
- Could not check Next.js security advisories. Continuing…
- Could not determine the latest stable Next.js version.
- Could not prepare adoption documents for
AI-assisted analysis of vercel/next.js@34433fd12e (2026-09-20).
Data as JSON: /api/errors/9cd373a710e99d16.
Report an issue: GitHub.
Appendix: source
Thrown at packages/next/src/lib/upgrade/prepare-upgrade.ts:205
async function fetchJSON(
url: string,
init: RequestInit | undefined = undefined
): Promise<{ value: unknown; headers: Headers }> {
try {
const response = await fetch(url, {
...init,
headers: { Accept: 'application/json', ...init?.headers },
signal: AbortSignal.timeout(10_000),
redirect: 'error',
})
if (!response.ok) {
throw new Error(`HTTP ${response.status}`)
}
return { value: await response.json(), headers: response.headers }
} catch (error) {
throw new Error('Could not fetch upgrade metadata. Please try again.', {
cause: error,
})
}
}
function parseReleases(value: unknown): SecuritySnapshot['releases'] {
const data = value as {
versions:
| Record<
string,
{
version: string
}
>
| undefined
}
if (!data?.versions) {View on GitHub (pinned to 34433fd12e)