{"record":{"id":"f66bbcd814d83144","repo":"wagoodman/dive","slug":"could-not-find-image-manifest","errorCode":null,"errorMessage":"could not find image manifest","messagePattern":"could not find image manifest","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"dive/image/docker/image_archive.go","lineNumber":177,"sourceCode":"\t\t\t}\n\t\t}\n\t}\n\n\tmanifestContent, exists := jsonFiles[\"manifest.json\"]\n\tif exists {\n\t\timg.manifest = newManifest(manifestContent)\n\t} else {\n\t\t// manifest.json is not part of the OCI spec, docker includes it for compatibility\n\t\t// Provide compatibility by finding the config and using our layerMap\n\t\tvar configPath string\n\t\tfor path, content := range jsonFiles {\n\t\t\tif isConfig(content) {\n\t\t\t\tconfigPath = path\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tif len(configPath) == 0 {\n\t\t\treturn img, fmt.Errorf(\"could not find image manifest\")\n\t\t}\n\n\t\tvar layerPaths []string\n\t\tfor k := range img.layerMap {\n\t\t\tlayerPaths = append(layerPaths, k)\n\t\t}\n\t\timg.manifest = manifest{\n\t\t\tConfigPath:    configPath,\n\t\t\tLayerTarPaths: layerPaths,\n\t\t}\n\t}\n\n\tconfigContent, exists := jsonFiles[img.manifest.ConfigPath]\n\tif !exists {\n\t\treturn img, fmt.Errorf(\"could not find image config\")\n\t}\n\n\timg.config = newConfig(configContent)","sourceCodeStart":159,"sourceCodeEnd":195,"githubUrl":"https://github.com/wagoodman/dive/blob/d6c691947f8fda635c952a17ee3b7555379d58f0/dive/image/docker/image_archive.go#L159-L195","documentation":"Returned by the docker-archive loader (dive/image/docker/image_archive.go:177) when the tar contains no manifest.json and no file passes the isConfig() heuristic among the *.json entries. Docker-saved archives include manifest.json for compatibility; pure OCI layout archives do not, so dive scans JSON files for a config-looking blob (rootfs/history fields) - if none matches, you get 'could not find image manifest'.","triggerScenarios":"Loading an OCI image layout (as produced by crane/kaniko/skopeo copy dir form or registry extraction) that lacks manifest.json and whose config blob is not discoverable by the heuristic; archives repacked by tools that drop or rename JSON metadata; truncated saves missing the manifest entry.","commonSituations":"CI artifacts built with buildkit/kaniko exported as OCI tars, then analyzed with dive without conversion; images copied out of registries with skopeo in OCI format; hand-modified tarballs.","solutions":["Convert the OCI archive to docker format: skopeo copy oci-archive:app.tar docker-archive:app-docker.tar app:tag, then analyze the converted tar","Or re-save through an engine: docker load/pull the image and docker save it (produces manifest.json)","Check the tar actually lacks manifest.json: tar -tf app.tar | grep manifest.json","Prefer analyzing via the engine (dive docker://<image>) when conversion is awkward"],"exampleFix":"# before (OCI-layout tar from kaniko)\ndive docker-archive://app-oci.tar   # could not find image manifest\n\n# after\nskopeo copy oci-archive:app-oci.tar docker-archive:app.tar:app:ci\ndive docker-archive://app.tar","handlingStrategy":"fallback","validationCode":"// detect OCI-only archives before analysis\nfunc hasDockerManifest(tarPath string) bool {\n    f, err := os.Open(tarPath)\n    if err != nil {\n        return false\n    }\n    defer f.Close()\n    tr := tar.NewReader(f)\n    for {\n        hdr, err := tr.Next()\n        if err != nil {\n            return false\n        }\n        if filepath.Base(hdr.Name) == \"manifest.json\" {\n            return true\n        }\n    }\n}\n\nif !hasDockerManifest(p) {\n    // convert or go through an engine instead of failing later\n}","typeGuard":null,"tryCatchPattern":"img, err := resolver.Fetch(ctx, \"docker-archive://\"+p)\nif err != nil && strings.Contains(err.Error(), \"could not find image manifest\") {\n    // fallback: convert OCI -> docker archive with skopeo\n    _ = exec.Command(\"skopeo\", \"copy\", \"oci-archive:\"+p, \"docker-archive:\"+p+\".docker.tar:tmp:ci\").Run()\n    img, err = resolver2.Fetch(ctx, \"docker-archive://\"+p+\".docker.tar\")\n}\nif err != nil {\n    return err\n}","preventionTips":["Standardize CI artifacts on docker-save format (manifest.json included)","Convert OCI layouts with skopeo before analysis","Check 'tar -tf img.tar | grep manifest.json' as a cheap smoke test"],"tags":["docker-archive","oci","manifest","format-mismatch"],"backgroundTag":null,"analyzedSha":"d6c691947f8fda635c952a17ee3b7555379d58f0","analyzedAt":"2026-08-15T09:42:35.293Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}