containerd/containerd · error

authorization server did not include a token in the response

Error message

authorization server did not include a token in the response

What it means

ErrNoToken is returned when a registry's token endpoint answered HTTP 200 but the JSON body contains no token/access_token field, so the client has no bearer token to authenticate subsequent requests.

Source

Thrown at core/remotes/docker/auth/fetch.go:38

	"context"
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"net/url"
	"strings"
	"time"

	remoteserrors "github.com/containerd/containerd/v2/core/remotes/errors"
	"github.com/containerd/containerd/v2/pkg/tracing"
	"github.com/containerd/containerd/v2/version"
	"github.com/containerd/log"
)

var (
	// ErrNoToken is returned if a request is successful but the body does not
	// contain an authorization token.
	ErrNoToken = errors.New("authorization server did not include a token in the response")
)

// GenerateTokenOptions generates options for fetching a token based on a challenge
func GenerateTokenOptions(ctx context.Context, host, username, secret string, c Challenge) (TokenOptions, error) {
	realm, ok := c.Parameters["realm"]
	if !ok {
		return TokenOptions{}, errors.New("no realm specified for token auth challenge")
	}

	realmURL, err := url.Parse(realm)
	if err != nil {
		return TokenOptions{}, fmt.Errorf("invalid token auth challenge realm: %w", err)
	}

	to := TokenOptions{
		Realm:    realmURL.String(),
		Service:  c.Parameters["service"],
		Username: username,

View on GitHub (pinned to 4246446a2b)

Solutions

  1. Inspect the raw token endpoint response (curl the realm URL with the same params) and fix the auth server so it returns a token/access_token field
  2. Check the realm in the WWW-Authenticate challenge points at the correct token service
  3. Update containerd — newer versions accept access_token as well as token fields
  4. If a proxy is in the path, verify it isn't mangling or truncating the response body

Example fix

// before: server returns {"expires_in":3600} with no token
// after (server side) ensure response includes token
{"token":"eyJhbGciOi...","expires_in":3600}
Defensive patterns

Strategy: try-catch

Validate before calling

// probe the token endpoint before pulling
resp, _ := http.Get(tokenRealmURL)
var body map[string]any
json.NewDecoder(resp.Body).Decode(&body)
if body["token"] == nil && body["access_token"] == nil {
    return errors.New("token server returns no token field")
}

Try / catch

_, err := auth.FetchToken(ctx, client, to, opts)
if errors.Is(err, auth.ErrNoToken) {
    return fmt.Errorf("registry auth server misconfigured (no token in 200 body): %w", err)
}

Prevention

When it happens

Trigger: Calling FetchToken or FetchTokenWithOAuth against a registry whose token server responds successfully but omits 'token'/'access_token' in the JSON body (e.g. misconfigured token service, error-in-200 responses, non-standard auth servers like some Artifactory/Quay setups).

Common situations: Misconfigured proxy or auth middleware stripping fields; registry returning an HTML/empty body with 200; OAuth device/refresh flows where the server only returns refresh tokens without access_token.

Related errors


AI-assisted analysis of containerd/containerd@4246446a2b (2026-09-02). Data as JSON: /api/errors/44f06b298408d5a0. Report an issue: GitHub.