Crosstalk-Solutions/project-nomad · error · Error
Failed to get auth token from ${registry}: ${response.status
Error message
Failed to get auth token from ${registry}: ${response.status} What it means
Thrown by ContainerRegistryService.getToken when the HTTP response from a registry's v2 token endpoint (e.g. https://<registry>/token?service=...&scope=repository:<name>:pull) returns a non-OK status, after fetchWithRetry retries were exhausted. The status code is embedded in the message (401, 404, 429, 5xx each imply different causes).
Source
Thrown at admin/app/services/container_registry_service.ts:101
const cacheKey = `${registry}/${fullName}`
const cached = this.tokenCache.get(cacheKey)
if (cached && cached.expiresAt > Date.now()) {
return cached.token
}
let tokenUrl: string
if (registry === 'registry-1.docker.io') {
tokenUrl = `https://auth.docker.io/token?service=registry.docker.io&scope=repository:${fullName}:pull`
} else if (registry === 'ghcr.io') {
tokenUrl = `https://ghcr.io/token?service=ghcr.io&scope=repository:${fullName}:pull`
} else {
// For other registries, try the standard v2 token endpoint
tokenUrl = `https://${registry}/token?service=${registry}&scope=repository:${fullName}:pull`
}
const response = await this.fetchWithRetry(tokenUrl)
if (!response.ok) {
throw new Error(`Failed to get auth token from ${registry}: ${response.status}`)
}
const data = (await response.json()) as { token?: string; access_token?: string }
const token = data.token || data.access_token || ''
if (!token) {
throw new Error(`No token returned from ${registry}`)
}
// Cache for 5 minutes (tokens usually last longer, but be conservative)
this.tokenCache.set(cacheKey, {
token,
expiresAt: Date.now() + 5 * 60 * 1000,
})
return token
}
View on GitHub (pinned to 0bd1c6f4f9)
Solutions
- Map the embedded status: 401/403 → repository doesn't exist or needs credentials (pass an auth token or Bearer); 404 → registry needs a custom token URL, extend the per-registry branch; 429 → back off and respect rate limits; 5xx → retry later
- Test the URL manually: curl 'https://<registry>/token?service=<registry>&scope=repository:<image>:pull' to see the raw response
- For private registries, supply credentials to the fetch instead of anonymous token requests
- Add per-registry overrides in getToken's registry-specific branches for non-standard auth endpoints
Example fix
// before
const response = await this.fetchWithRetry(tokenUrl)
if (!response.ok) {
throw new Error(`Failed to get auth token from ${registry}: ${response.status}`)
}
// after
const response = await this.fetchWithRetry(tokenUrl)
if (!response.ok) {
const body = await response.text().catch(() => '')
throw new Error(`Failed to get auth token from ${registry}: ${response.status} ${body.slice(0, 200)}`)
} Defensive patterns
Strategy: retry
Validate before calling
const res = await fetch(tokenUrl) if (res.status === 404 || res.status === 401) configureCustomAuthFor(registry) // avoid guaranteed-failing calls if (res.status === 429) await waitForRateLimitWindow()
Type guard
function isRetryableTokenStatus(status: number): boolean {
return status === 429 || status >= 500
} Try / catch
try { const t = await registrySvc.getToken(...) } catch (e) { if (/Failed to get auth token.*:(429|5\d\d)/.test(e.message)) { await sleep(backoff); retry() } else if (/:(401|403)/.test(e.message)) promptForCredentials() else throw e } Prevention
- Cache tokens (the service already caches 5 min) to reduce endpoint hits
- Add per-registry auth URL overrides for non-standard registries
- Respect Retry-After on 429s in fetchWithRetry
- Include response body in the thrown error for easier triage
When it happens
Trigger: Pulling metadata for an image on a registry whose auth endpoint rejects the request: 401 for an unknown/forbidden repository on Docker Hub, 404 when the token endpoint path is wrong for that registry (non-standard registries), 429 rate limiting, or 5xx during registry outages.
Common situations: Private/not-well-known registries that don't implement the standard /token endpoint, Docker Hub rate limits on anonymous token requests, corporate proxies returning 407/401, misspelled image names, or transient registry incidents.
Related errors
- No token returned from ${registry}
- HTTP ${resp.status} from ${MANIFEST_URL}
- sysbench disk-write benchmark produced no parseable MiB/s —
- Sysbench command failed: ${error.message}
- recreated container ${readiness.reason}
AI-assisted analysis of Crosstalk-Solutions/project-nomad@0bd1c6f4f9 (2026-08-27).
Data as JSON: /api/errors/7c0d3908591c0db3.
Report an issue: GitHub.