{"record":{"id":"eeb2c050c0d798cb","repo":"GoogleContainerTools/jib","slug":"cannot-rewrite-blob-backed-by-an-inputstream","errorCode":null,"errorMessage":"Cannot rewrite Blob backed by an InputStream","messagePattern":"Cannot rewrite Blob backed by an InputStream","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"jib-core/src/main/java/com/google/cloud/tools/jib/blob/InputStreamBlob.java","lineNumber":40,"sourceCode":"import java.io.OutputStream;\n\n/** A {@link Blob} that holds an {@link InputStream}. */\nclass InputStreamBlob implements Blob {\n\n  private final InputStream inputStream;\n\n  /** Indicates if the {@link Blob} has already been written or not. */\n  private boolean isWritten = false;\n\n  InputStreamBlob(InputStream inputStream) {\n    this.inputStream = inputStream;\n  }\n\n  @Override\n  public BlobDescriptor writeTo(OutputStream outStream) throws IOException {\n    // Cannot rewrite.\n    if (isWritten) {\n      throw new IllegalStateException(\"Cannot rewrite Blob backed by an InputStream\");\n    }\n    try (InputStream inStream = this.inputStream) {\n      return Digests.computeDigest(inStream, outStream);\n\n    } finally {\n      isWritten = true;\n    }\n  }\n\n  @Override\n  public boolean isRetryable() {\n    return false;\n  }\n}\n","sourceCodeStart":22,"sourceCodeEnd":55,"githubUrl":"https://github.com/GoogleContainerTools/jib/blob/fb949e2676afbbd7dd7a1ef61e20251931325654/jib-core/src/main/java/com/google/cloud/tools/jib/blob/InputStreamBlob.java#L22-L55","documentation":"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'.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Create a fresh InputStreamBlob (new InputStream) for each write attempt.","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.","Wrap retry logic so each retry constructs the Blob anew instead of reusing it.","Use blob.toBlobDescriptor()/digest once and cache the descriptor rather than re-reading the stream."],"exampleFix":"// before\nBlob blob = Blobs.from(inputStream);\nblob.writeTo(out1);\nblob.writeTo(out2); // IllegalStateException\n// after\nbyte[] bytes = ByteStreams.toByteArray(inputStream);\nBlob blob = Blobs.from(bytes); // replayable\nblob.writeTo(out1);\nblob.writeTo(out2);","handlingStrategy":"fallback","validationCode":"null","typeGuard":"null","tryCatchPattern":"try { blob.writeTo(out); } catch (IllegalStateException e) { /* blob was stream-backed and already consumed; rebuild blob */ }","preventionTips":["Prefer byte[]/Path-backed Blobs when content may be written more than once","Create a new InputStreamBlob per write attempt in retry loops","Compute the descriptor once and cache it (toBlobDescriptor) instead of re-reading","Never pass raw request InputStreams into blobs you might reuse"],"tags":["stream","state","retry"],"backgroundTag":"invalid-state-transition","analyzedSha":"fb949e2676afbbd7dd7a1ef61e20251931325654","analyzedAt":"2026-09-06T14:04:09.491Z","contentChangedAt":"2026-09-06T14:04:09.491Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}