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

  1. 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
  2. Test the URL manually: curl 'https://<registry>/token?service=<registry>&scope=repository:<image>:pull' to see the raw response
  3. For private registries, supply credentials to the fetch instead of anonymous token requests
  4. 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

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


AI-assisted analysis of Crosstalk-Solutions/project-nomad@0bd1c6f4f9 (2026-08-27). Data as JSON: /api/errors/7c0d3908591c0db3. Report an issue: GitHub.