{"record":{"id":"f0dacf68674df3f1","repo":"GoogleContainerTools/jib","slug":"push-may-fail-with-pull-only-bearer-auth-token","errorCode":null,"errorMessage":"push may fail with pull-only bearer auth token","messagePattern":"push may fail with pull-only bearer auth token","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"jib-core/src/main/java/com/google/cloud/tools/jib/registry/RegistryClient.java","lineNumber":454,"sourceCode":"\n  public ManifestAndDigest<ManifestTemplate> pullManifest(String imageQualifier)\n      throws IOException, RegistryException {\n    return pullManifest(imageQualifier, ManifestTemplate.class);\n  }\n\n  /**\n   * Pushes the image manifest for a specific tag.\n   *\n   * @param manifestTemplate the image manifest\n   * @param imageTag the tag to push on\n   * @return the digest of the pushed image\n   * @throws IOException if communicating with the endpoint fails\n   * @throws RegistryException if communicating with the endpoint fails\n   */\n  public DescriptorDigest pushManifest(ManifestTemplate manifestTemplate, String imageTag)\n      throws IOException, RegistryException {\n    if (isBearerAuth(authorization.get()) && readOnlyBearerAuth) {\n      throw new IllegalStateException(\"push may fail with pull-only bearer auth token\");\n    }\n\n    return callRegistryEndpoint(\n        new ManifestPusher(\n            registryEndpointRequestProperties, manifestTemplate, imageTag, eventHandlers));\n  }\n\n  /**\n   * Check if a blob is on the registry.\n   *\n   * @param blobDigest the blob digest to check for\n   * @return the BLOB's {@link BlobDescriptor} if the BLOB exists on the registry, or {@link\n   *     Optional#empty()} if it doesn't\n   * @throws IOException if communicating with the endpoint fails\n   * @throws RegistryException if communicating with the endpoint fails\n   */\n  public Optional<BlobDescriptor> checkBlob(DescriptorDigest blobDigest)\n      throws IOException, RegistryException {","sourceCodeStart":436,"sourceCodeEnd":472,"githubUrl":"https://github.com/GoogleContainerTools/jib/blob/fb949e2676afbbd7dd7a1ef61e20251931325654/jib-core/src/main/java/com/google/cloud/tools/jib/registry/RegistryClient.java#L436-L472","documentation":"RegistryClient.pushManifest refuses to push a manifest when the client is using Bearer authentication with a token that was obtained with pull-only (read-only) scopes. Since the token lacks push scope, the push would fail at the registry, so Jib fails fast with IllegalStateException.","triggerScenarios":"Calling pushManifest (or constructing a RegistryClient via factory methods like toOtherRepository) on a client whose authorization is Bearer and readOnlyBearerAuth is true — i.e., the token was fetched without push (pull,push) repository scopes.","commonSituations":"Building/pushing using a registry client initialized for pulling (e.g., toOtherRegistry/pull-oriented API) and then attempting to push; using an access token scoped read-only; misconfigured service account/token with only pull permissions (e.g., GCR pull-only service account).","solutions":["Obtain a RegistryClient via a factory that requests push scopes (e.g., RegistryClient.factory(...).setCredential... / toOtherRepository with push intent) so the token is requested with pull,push scope","Use credentials/token with push permissions (service account or IAM role granting write)","If you only intend to pull, do not call pushManifest on this client"],"exampleFix":"// before: read-only client used for push\nRegistryClient client = new RegistryClient(credential, serverUrl, imageName, eventHandlers);\nclient.configureReadOnlyBearerAuth();\nclient.pushManifest(manifest, tag);\n// after: client authorized for push\nRegistryClient client = new RegistryClient(credential, serverUrl, imageName, eventHandlers); // token fetched with push scope\nclient.pushManifest(manifest, tag);","handlingStrategy":"validation","validationCode":"// ensure the token has push scope before attempting a push\n// (Jib guards this internally; pre-validate your service account's scopes in CI)\nassertPushScopeGranted(registry, serviceAccount);","typeGuard":null,"tryCatchPattern":"try { client.pushManifest(manifest, tag); } catch (IllegalStateException e) { // client is pull-only; obtain a push-scoped client\n  newPushScopedClient().pushManifest(manifest, tag); }","preventionTips":["Use one client per intent: pull clients for pulls, push-scoped for pushes","Verify service accounts/IAM roles grant write before CI push jobs","Do not combine configureReadOnlyBearerAuth with push calls"],"tags":["registry","push","authorization"],"backgroundTag":"insufficient-permissions","analyzedSha":"fb949e2676afbbd7dd7a1ef61e20251931325654","analyzedAt":"2026-09-06T14:04:09.491Z","contentChangedAt":"2026-09-06T14:04:09.491Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}