{"record":{"id":"c908970bddf9ace9","repo":"GoogleContainerTools/jib","slug":"the-pulled-blob-has-digest-receiveddigest-bu","errorCode":null,"errorMessage":"The pulled BLOB has digest '${receivedDigest}', but the request digest was '${requestedDigest}'","messagePattern":"The pulled BLOB has digest '(.+?)', but the request digest was '(.+?)'","errorType":"exception","errorClass":"UnexpectedBlobDigestException","httpStatus":null,"severity":"error","filePath":"jib-core/src/main/java/com/google/cloud/tools/jib/registry/BlobPuller.java","lineNumber":75,"sourceCode":"      Consumer<Long> writtenByteCountListener) {\n    this.registryEndpointRequestProperties = registryEndpointRequestProperties;\n    this.blobDigest = blobDigest;\n    this.destinationOutputStream = destinationOutputStream;\n    this.blobSizeListener = blobSizeListener;\n    this.writtenByteCountListener = writtenByteCountListener;\n  }\n\n  @Override\n  public Void handleResponse(Response response) throws IOException, UnexpectedBlobDigestException {\n    blobSizeListener.accept(response.getContentLength());\n\n    try (OutputStream outputStream =\n        new NotifyingOutputStream(destinationOutputStream, writtenByteCountListener)) {\n      BlobDescriptor receivedBlobDescriptor =\n          Digests.computeDigest(response.getBody(), outputStream);\n\n      if (!blobDigest.equals(receivedBlobDescriptor.getDigest())) {\n        throw new UnexpectedBlobDigestException(\n            \"The pulled BLOB has digest '\"\n                + receivedBlobDescriptor.getDigest()\n                + \"', but the request digest was '\"\n                + blobDigest\n                + \"'\");\n      }\n    }\n\n    return null;\n  }\n\n  @Override\n  @Nullable\n  public BlobHttpContent getContent() {\n    return null;\n  }\n\n  @Override","sourceCodeStart":57,"sourceCodeEnd":93,"githubUrl":"https://github.com/GoogleContainerTools/jib/blob/fb949e2676afbbd7dd7a1ef61e20251931325654/jib-core/src/main/java/com/google/cloud/tools/jib/registry/BlobPuller.java#L57-L93","documentation":"BlobPuller verifies that the BLOB downloaded from the registry matches the digest that was requested. When the computed digest of the streamed response body differs from the requested blobDigest, it throws UnexpectedBlobDigestException. This protects against corrupted, truncated, or tampered downloads.","triggerScenarios":"Calling BlobPuller.handleResponse with a registry response whose body hashes to a digest different from the blobDigest requested in the pull.","commonSituations":"A proxy or transparent cache corrupts or truncates the layer download; the registry serves a different layer than referenced (mis-cached CDN edge); a MITM or storage corruption in the registry backend.","solutions":["Retry the pull; transient proxy/CDN corruption is often intermittent","Clear or bypass intermediary caches (HTTP proxy, registry mirror) and pull again","Verify the manifest digest referenced actually exists in the registry (re-push the image if it was partially uploaded)","Check network stability / TLS interception devices that may alter bytes"],"exampleFix":"// before: pulling through a flaky mirror\nRegistryClient client = new RegistryClient(null, \"mirror.example.com\", \"my/repo\", eventHandlers);\nclient.pullBlob(digest, consumer, writer);\n// after: pull from the authoritative registry and retry on digest mismatch\nRegistryClient client = new RegistryClient(null, \"registry-1.docker.io\", \"my/repo\", eventHandlers);\ntry {\n  client.pullBlob(digest, consumer, writer);\n} catch (UnexpectedBlobDigestException e) {\n  client.pullBlob(digest, consumer, writer); // retry once\n}","handlingStrategy":"retry","validationCode":"// verify digest locally if you already have the bytes\nDigests.computeDigest(blob).getDigest().equals(expectedDigest)","typeGuard":null,"tryCatchPattern":"try { client.pullBlob(digest, sink, out); } catch (UnexpectedBlobDigestException e) { log.warn(\"digest mismatch, retrying\"); client.pullBlob(digest, sink, out); }","preventionTips":["Avoid untrusted proxies/mirrors between build and registry","Prefer HTTPS end-to-end to prevent byte-altering middleboxes","Pin digests and re-pull on mismatch rather than proceeding"],"tags":["registry","blob","digest","integrity"],"backgroundTag":"checksum-mismatch","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"}