GoogleContainerTools/jib · error · RegistryUnauthorizedException

Unauthorized for ${serverUrl}/${imageName}

Error message

Unauthorized for ${serverUrl}/${imageName}

What it means

RegistryEndpointCaller translates registry HTTP 401 responses into RegistryUnauthorizedException ('Unauthorized for <serverUrl>/<imageName>') when credentials were present but rejected. If the response indicates authorization was cleared, it instead throws RegistryCredentialsNotSentException; this error is the missing/wrong-credentials case.

Source

Thrown at jib-core/src/main/java/com/google/cloud/tools/jib/registry/RegistryEndpointCaller.java:156

        httpClient.call(registryEndpointProvider.getHttpMethod(), url, requestBuilder.build())) {

      return registryEndpointProvider.handleResponse(response);

    } catch (ResponseException ex) {
      // First, see if the endpoint provider handles an exception as an expected response.
      try {
        return registryEndpointProvider.handleHttpResponseException(ex);

      } catch (ResponseException responseException) {
        if (responseException.getStatusCode() == HttpStatusCodes.STATUS_CODE_BAD_REQUEST
            || responseException.getStatusCode() == HttpStatusCodes.STATUS_CODE_NOT_FOUND
            || responseException.getStatusCode()
                == HttpStatusCodes.STATUS_CODE_METHOD_NOT_ALLOWED) {
          // The name or reference was invalid.
          throw newRegistryErrorException(responseException);

        } else if (responseException.getStatusCode() == HttpStatusCodes.STATUS_CODE_FORBIDDEN) {
          throw new RegistryUnauthorizedException(serverUrl, imageName, responseException);

        } else if (responseException.getStatusCode() == HttpStatusCodes.STATUS_CODE_UNAUTHORIZED) {
          if (responseException.requestAuthorizationCleared()) {
            throw new RegistryCredentialsNotSentException(serverUrl, imageName);
          } else {
            // Credentials are either missing or wrong.
            throw new RegistryUnauthorizedException(serverUrl, imageName, responseException);
          }

        } else {
          // Unknown
          throw responseException;
        }
      }

    } catch (IOException ex) {
      logError("I/O error for image [" + serverUrl + "/" + imageName + "]:");
      logError("    " + ex.getClass().getName());

View on GitHub (pinned to fb949e2676)

Solutions

  1. Re-run 'docker login <registry>' or refresh the credential helper's token
  2. Verify the account has the needed scope for the exact repository (pull vs push vs cross-mount)
  3. If using Jib's auth parameters (authUsername/authPassword), confirm they are correct and not expired
  4. For cross-repo blob mounts, ensure access to the source repository or disable cross-repo mounts

Example fix

// before: expired token in CI
docker login registry.example.com // with stale creds
// after: refresh credentials before build
echo "$NEW_TOKEN" | docker login registry.example.com -u oauth2accesstoken --password-stdin
Defensive patterns

Strategy: try-catch

Validate before calling

// verify credentials work against the registry API before the build
curl -s -o /dev/null -w '%{http_code}' -u user:pass https://registry.example.com/v2/my/repo/manifests/latest

Try / catch

try { ...registry ops...; } catch (RegistryUnauthorizedException e) { throw new BuildException("Credentials rejected for " + registry + "/" + repo + " — re-login", e); }

Prevention

When it happens

Trigger: A registry API call (manifest pull/push, blob pull/push) receives HTTP 401 (STATUS_CODE_UNAUTHORIZED) from the registry without the requestAuthorizationCleared flag during call().

Common situations: Expired or wrong credentials in docker config/credential helper; token lacks required scope (e.g., pulling from another repo's namespace); service account revoked; pushing to a private repo with anonymous credentials.

Understand the failure class

Related errors


AI-assisted analysis of GoogleContainerTools/jib@fb949e2676 (2026-09-06). Data as JSON: /api/errors/06c384e624a4dd5c. Report an issue: GitHub.