{"record":{"id":"833b2d52df3d91e3","repo":"GoogleContainerTools/jib","slug":"invalid-base-image-format-manifest-contains-n-l","errorCode":null,"errorMessage":"Invalid base image format: manifest contains <n> layers, but container configuration contains <n> layers","messagePattern":"Invalid base image format: manifest contains <n> layers, but container configuration contains <n> layers","errorType":"exception","errorClass":"LayerCountMismatchException","httpStatus":null,"severity":"error","filePath":"jib-core/src/main/java/com/google/cloud/tools/jib/builder/steps/LocalBaseImageSteps.java","lineNumber":237,"sourceCode":"      try (InputStream manifestStream =\n          Files.newInputStream(destination.resolve(\"manifest.json\"))) {\n        loadManifest =\n            JsonMapper.builder()\n                .configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true)\n                .build()\n                .readValue(manifestStream, DockerManifestEntryTemplate[].class)[0];\n      }\n\n      Path configPath = destination.resolve(loadManifest.getConfig());\n      ContainerConfigurationTemplate configurationTemplate =\n          JsonTemplateMapper.readJsonFromFile(configPath, ContainerConfigurationTemplate.class);\n      // Don't compute the digest of the loaded Java JSON instance.\n      BlobDescriptor originalConfigDescriptor =\n          Blobs.from(configPath).writeTo(ByteStreams.nullOutputStream());\n\n      List<String> layerFiles = loadManifest.getLayerFiles();\n      if (configurationTemplate.getLayerCount() != layerFiles.size()) {\n        throw new LayerCountMismatchException(\n            \"Invalid base image format: manifest contains \"\n                + layerFiles.size()\n                + \" layers, but container configuration contains \"\n                + configurationTemplate.getLayerCount()\n                + \" layers\");\n      }\n      buildContext\n          .getBaseImageLayersCache()\n          .writeLocalConfig(originalConfigDescriptor.getDigest(), configurationTemplate);\n\n      // Check the first layer to see if the layers are compressed already. 'docker save' output\n      // is uncompressed, but a jib-built tar has compressed layers.\n      boolean layersAreCompressed =\n          !layerFiles.isEmpty() && isGzipped(destination.resolve(layerFiles.get(0)));\n\n      // Process layer blobs\n      try (ProgressEventDispatcher progressEventDispatcher =\n          progressEventDispatcherFactory.create(","sourceCodeStart":219,"sourceCodeEnd":255,"githubUrl":"https://github.com/GoogleContainerTools/jib/blob/fb949e2676afbbd7dd7a1ef61e20251931325654/jib-core/src/main/java/com/google/cloud/tools/jib/builder/steps/LocalBaseImageSteps.java#L219-L255","documentation":"When loading a base image from a Docker tar archive (cacheDockerImageTar), Jib cross-checks the number of layer files listed in the tar's manifest with the layer count in the container configuration JSON. A mismatch means the tar is malformed or inconsistent, so LayerCountMismatchException is thrown with both counts. This guards against producing a broken image built on inconsistent metadata.","triggerScenarios":"Running `jib build --image=...` with a base image passed via `docker:` tarball (or fromDaemon) whose manifest.json lists a different number of layers than the config JSON's rootfs diff_ids - e.g. manually edited tars, tars produced by non-Docker tools, or partially extracted tars.","commonSituations":"Building from a `docker save` output that was modified or truncated; third-party image tarball generators producing inconsistent manifests; copying/tarring images incorrectly (missing layer files); older OCI/Docker format variants.","solutions":["Regenerate the tarball: `docker save <image> -o base.tar` from an intact Docker daemon and retry.","Verify the tar integrity: manifest.json layer count vs config JSON diff_ids; restore missing layer files.","Use a registry base image reference (e.g. eclipse-temurin:17) instead of a local tar to bypass tar loading.","Re-pull the base image (`docker pull`) to replace a corrupt local image, then re-save.","If using a third-party tool to build tars, fix or upgrade that tool to emit matching manifest/config."],"exampleFix":"// before\njib.image = \"docker://custom-base.tar\" // tar with mismatched layers\n// after\n// regenerate cleanly\ndocker pull eclipse-temurin:17\ndocker save eclipse-temurin:17 -o base.tar\njib.image = \"docker://base.tar\"","handlingStrategy":"validation","validationCode":"// Inspect tar before handing to jib\nint manifestLayers = readManifestLayerCount(baseTar); // from manifest.json\nint configLayers = readConfigDiffIdCount(baseTar);    // from config JSON rootfs.diff_ids\nif (manifestLayers != configLayers) throw new IllegalArgumentException(\"corrupt base tar\");","typeGuard":"null","tryCatchPattern":"try { /* jib build from docker tar */ } catch (LayerCountMismatchException e) { /* regenerate tarball with docker save */ }","preventionTips":["Always produce base tarballs with `docker save` from an intact image","Never hand-edit or truncate saved tars; keep layers alongside manifest.json","Prefer registry base image references over local tar files","Re-pull images that fail, to replace corrupt local data"],"tags":["docker-tar","base-image","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-14T00:17:10.932Z"}