GoogleContainerTools/jib · error · IllegalStateException

Cannot rewrite Blob backed by an InputStream

Error message

Cannot rewrite Blob backed by an InputStream

What it means

An InputStreamBlob streams its content once (computing its digest while writing). Because the underlying InputStream is consumed and closed on first writeTo, the Blob cannot be written a second time; a repeated writeTo throws IllegalStateException 'Cannot rewrite Blob backed by an InputStream'.

Source

Thrown at jib-core/src/main/java/com/google/cloud/tools/jib/blob/InputStreamBlob.java:40

import java.io.OutputStream;

/** A {@link Blob} that holds an {@link InputStream}. */
class InputStreamBlob implements Blob {

  private final InputStream inputStream;

  /** Indicates if the {@link Blob} has already been written or not. */
  private boolean isWritten = false;

  InputStreamBlob(InputStream inputStream) {
    this.inputStream = inputStream;
  }

  @Override
  public BlobDescriptor writeTo(OutputStream outStream) throws IOException {
    // Cannot rewrite.
    if (isWritten) {
      throw new IllegalStateException("Cannot rewrite Blob backed by an InputStream");
    }
    try (InputStream inStream = this.inputStream) {
      return Digests.computeDigest(inStream, outStream);

    } finally {
      isWritten = true;
    }
  }

  @Override
  public boolean isRetryable() {
    return false;
  }
}

View on GitHub (pinned to fb949e2676)

Solutions

  1. Create a fresh InputStreamBlob (new InputStream) for each write attempt.
  2. Buffer the content first, e.g. Blobs.from(byte[]) or write the stream to a temp file and use a file-backed Blob, which is replayable.
  3. Wrap retry logic so each retry constructs the Blob anew instead of reusing it.
  4. Use blob.toBlobDescriptor()/digest once and cache the descriptor rather than re-reading the stream.

Example fix

// before
Blob blob = Blobs.from(inputStream);
blob.writeTo(out1);
blob.writeTo(out2); // IllegalStateException
// after
byte[] bytes = ByteStreams.toByteArray(inputStream);
Blob blob = Blobs.from(bytes); // replayable
blob.writeTo(out1);
blob.writeTo(out2);
Defensive patterns

Strategy: fallback

Validate before calling

null

Type guard

null

Try / catch

try { blob.writeTo(out); } catch (IllegalStateException e) { /* blob was stream-backed and already consumed; rebuild blob */ }

Prevention

When it happens

Trigger: Calling blob.writeTo(outputStream) twice on the same Blob created from an InputStream (e.g. via Blobs.from(InputStream)); retry logic that replays a failed upload by re-writing the same Blob; logging code that inspects content before the real upload.

Common situations: HTTP upload retries in RegistryClient flows reusing the same blob; piping a Blob both to a digest computation and then to the network; caching a Blob built from a request body InputStream and reusing it across attempts.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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