#4505·jib

jib-core: ReproducibleLayerBuilder blob improperly closes the output stream

Author: scottjassoCreated May 18, 2026Updated Aug 11, 2026
Labelstype:bugpriority: p3

Environment:

  • Jib version: jib-core: 0.28.1
  • Build tool: gradle
  • OS: Mac (irrelevant)

Description of the issue: ReproducibleLayerBuilder builds a blob that first calls close on the underlying OutputStream (contrary to the javadoc), and then later calls flush, causing many OutputStream implementations to throw.

  1. ReproducibleLayerBuilder uses TarStreamBuilder under the hood. It returns Blobs.from(tarStreamBuilder::writeAsTarArchiveTo, false), which creates a WriteableContentsBlob.
  2. TarStreamBuilder.writeAsTarArchiveTo uses try-with-resources, which closes the underlying OutputStream when finished.
  3. WriteableContentsBlob.writeTo uses Digests.computeDigest(writableContents, outputStream), which does this:
java
    CountingDigestOutputStream digestOutStream = new CountingDigestOutputStream(outStream);
    contents.writeTo(digestOutStream);
    digestOutStream.flush();
    return digestOutStream.computeDigest();

Line 2 closes the digestOutputStream (because of writeAsTarArchiveTo), so then line 3 flushes after close.

Expected behavior:

Blob.writeTo should never call close on the OutputStream. You should be able to use any OutputStream.

TarStreamBuilder should not close the output stream passed to it.

Steps to reproduce:

java
ReproducibleLayerBuilder blob = new ReproducibleLayerBuilder(ImmutableList.of()).build();
// this causes ZstdOutputStream to throw IOException: StreamClosed
blob.writeTo(new ZstdOutputStream(new ByteArrayOutputStream()));

Source: GoogleContainerTools/jib