{"record":{"id":"23d6c094136c2c3c","repo":"GoogleContainerTools/jib","slug":"cannot-run-jib-in-offline-mode-local-jib-cache-fo","errorCode":null,"errorMessage":"Cannot run Jib in offline mode; local Jib cache for base image is missing image layer <layerDigest>. Rerun Jib in online mode with \"-Djib.alwaysCacheBaseImage=true\" to re-download the base image layers.","messagePattern":"Cannot run Jib in offline mode; local Jib cache for base image is missing image layer <layerDigest>\\. Rerun Jib in online mode with \"-Djib\\.alwaysCacheBaseImage=true\" to re-download the base image layers\\.","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"jib-core/src/main/java/com/google/cloud/tools/jib/builder/steps/ObtainBaseImageLayerStep.java","lineNumber":129,"sourceCode":"\n      StateInTarget stateInTarget = blobExistenceChecker.check(layerDigest);\n      if (stateInTarget == StateInTarget.EXISTING) {\n        eventHandlers.dispatch(\n            LogEvent.info(\n                \"Skipping pull; BLOB already exists on target registry : \"\n                    + layer.getBlobDescriptor()));\n        return new PreparedLayer.Builder(layer).setStateInTarget(stateInTarget).build();\n      }\n\n      Cache cache = buildContext.getBaseImageLayersCache();\n\n      // Checks if the layer already exists in the cache.\n      Optional<CachedLayer> optionalCachedLayer = cache.retrieve(layerDigest);\n      if (optionalCachedLayer.isPresent()) {\n        CachedLayer cachedLayer = optionalCachedLayer.get();\n        return new PreparedLayer.Builder(cachedLayer).setStateInTarget(stateInTarget).build();\n      } else if (buildContext.isOffline()) {\n        throw new IOException(\n            \"Cannot run Jib in offline mode; local Jib cache for base image is missing image layer \"\n                + layerDigest\n                + \". Rerun Jib in online mode with \\\"-Djib.alwaysCacheBaseImage=true\\\" to \"\n                + \"re-download the base image layers.\");\n      }\n\n      try (ThrottledProgressEventDispatcherWrapper progressEventDispatcherWrapper =\n          new ThrottledProgressEventDispatcherWrapper(\n              progressEventDispatcher.newChildProducer(),\n              \"pulling base image layer \" + layerDigest)) {\n        CachedLayer cachedLayer =\n            cache.writeCompressedLayer(\n                Verify.verifyNotNull(registryClient)\n                    .pullBlob(\n                        layerDigest,\n                        progressEventDispatcherWrapper::setProgressTarget,\n                        progressEventDispatcherWrapper::dispatchProgress));\n        return new PreparedLayer.Builder(cachedLayer).setStateInTarget(stateInTarget).build();","sourceCodeStart":111,"sourceCodeEnd":147,"githubUrl":"https://github.com/GoogleContainerTools/jib/blob/fb949e2676afbbd7dd7a1ef61e20251931325654/jib-core/src/main/java/com/google/cloud/tools/jib/builder/steps/ObtainBaseImageLayerStep.java#L111-L147","documentation":"In offline mode Jib cannot download base image layers. When a base image layer digest is not present in the local Jib cache and the build is offline, ObtainBaseImageLayerStep.call throws IOException telling the user to rerun online (optionally with -Djib.alwaysCacheBaseImage=true) so layers are persisted in the project cache. This prevents builds from silently producing images missing layers.","triggerScenarios":"Running with `--offline=true` (or jib.offline=true) while the local cache lacks a layer of the base image - e.g. the base image digest changed upstream, the cache was cleared, or a pinned digest was never downloaded on this machine.","commonSituations":"CI jobs run in network-isolated sandboxes after cache eviction; air-gapped environments switching base image tags so the cached layers no longer match; using `docker://` metadata flows expecting the Jib cache (not the Docker daemon cache); first build on a new machine with --offline.","solutions":["Rerun once with network access: remove --offline / set jib.offline=false, and add -Djib.alwaysCacheBaseImage=true to populate the persistent cache, then go offline again.","Pin/use a base image whose layers are already in the local cache (previously downloaded).","Warm the cache on a connected machine and copy the Jib cache directory to the offline environment.","If available, build from a Docker daemon image (docker daemonizer or docker:// tar path) whose layers don't need the Jib cache.","Restore the base image layers by re-running the same build online with the same base image tag/digest."],"exampleFix":"// before (CI, offline runner)\n./gradlew jibBuild --offline\n// after\n./gradlew jibBuild -Djib.alwaysCacheBaseImage=true   // once, online, to warm cache\n// subsequent offline builds\n./gradlew jibBuild --offline","handlingStrategy":"retry","validationCode":"// Check cache before going offline\nfor (String digest : baseImageLayerDigests) {\n  if (cache.retrieve(digest).isEmpty()) throw new IllegalStateException(\"warm cache online first: \" + digest);\n}","typeGuard":"null","tryCatchPattern":"try { /* offline build */ } catch (IOException e) {\n  if (e.getMessage().contains(\"offline mode\")) { /* rerun online with -Djib.alwaysCacheBaseImage=true, then retry offline */ }\n  else throw e;\n}","preventionTips":["Warm the Jib cache with -Djib.alwaysCacheBaseImage=true before network-restricted CI runs","Pin base image tags/digests so cached layers stay valid","Avoid clearing the Jib cache on offline runners","Pre-copy the cache directory into air-gapped environments"],"tags":["offline","cache","base-image","ci"],"backgroundTag":"resource-not-found","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"}