{"record":{"id":"e4a13b5f0cc70516","repo":"wagoodman/dive","slug":"could-not-find-s-in-parsed-layers","errorCode":null,"errorMessage":"could not find '%s' in parsed layers","messagePattern":"could not find '(.+?)' in parsed layers","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"dive/image/docker/image_archive.go","lineNumber":260,"sourceCode":"\t\t\treturn nil, fmt.Errorf(\"unexpected tar file (XHeader): type=%v name=%s\", header.Typeflag, name)\n\t\tdefault:\n\t\t\tfiles = append(files, filetree.NewFileInfoFromTarHeader(tarReader, header, name))\n\t\t}\n\t}\n\treturn files, nil\n}\n\nfunc (img *ImageArchive) ToImage(id string) (*image.Image, error) {\n\ttrees := make([]*filetree.FileTree, 0)\n\n\t// build the content tree\n\tfor _, treeName := range img.manifest.LayerTarPaths {\n\t\ttr, exists := img.layerMap[treeName]\n\t\tif exists {\n\t\t\ttrees = append(trees, tr)\n\t\t\tcontinue\n\t\t}\n\t\treturn nil, fmt.Errorf(\"could not find '%s' in parsed layers\", treeName)\n\t}\n\n\t// build the layers array\n\tlayers := make([]*image.Layer, 0)\n\n\t// note that the engineResolver config stores images in reverse chronological order, so iterate backwards through layers\n\t// as you iterate chronologically through history (ignoring history items that have no layer contents)\n\t// Note: history is not required metadata in a docker image!\n\thistIdx := 0\n\tfor idx, tree := range trees {\n\t\t// ignore empty layers, we are only observing layers with content\n\t\thistoryObj := historyEntry{\n\t\t\tCreatedBy: \"(missing)\",\n\t\t}\n\t\tfor nextHistIdx := histIdx; nextHistIdx < len(img.config.History); nextHistIdx++ {\n\t\t\tif !img.config.History[nextHistIdx].EmptyLayer {\n\t\t\t\thistIdx = nextHistIdx\n\t\t\t\tbreak","sourceCodeStart":242,"sourceCodeEnd":278,"githubUrl":"https://github.com/wagoodman/dive/blob/d6c691947f8fda635c952a17ee3b7555379d58f0/dive/image/docker/image_archive.go#L242-L278","documentation":"ToImage() builds the image by looking up every layer tar path listed in manifest.json inside the layerMap that was previously parsed from the archive. If a manifest entry (e.g. 'blobs/sha256/...' or 'layer/layer.tar') has no matching key in layerMap, the manifest and the actually-parsed archive contents disagree and dive refuses to construct a half-built image.","triggerScenarios":"Calling ImageArchive.ToImage after NewImageArchive on an archive where manifest.json's Layers[] references paths that were not present (or were skipped) when the archive tar was walked: truncated or corrupt 'docker save' output, archives where layer file paths differ from the manifest keys (leading './', absolute paths, different casing), or OCI-layout archives whose manifest paths don't match docker-style layer paths the parser expected.","commonSituations":"Corrupted/partially downloaded image tar; archives produced by a different tool (skopeo/copied OCI dirs) whose manifest.json layer paths use a different prefix than the files in the tar; intermixing layers from two archives; disk-full during 'docker save'.","solutions":["Re-create the archive from the source engine: 'docker save <image> -o img.tar' (or 'podman image save') and retry","Verify the archive is intact: 'tar -tf img.tar' and confirm every path in manifest.json's Layers array exists inside the tar","Check for path-format drift: manifest entries must match the tar member names exactly (no extra './', no renamed blobs); rebuild the tar so they align","If the archive is OCI-layout rather than docker-save format, convert it first (e.g. 'skopeo copy oci:/path docker-archive:img.tar')"],"exampleFix":"# before\npackage main\nimport \"github.com/wagoodman/dive/image/docker\"\n\nimg, _ := docker.NewImageArchive(f)\nimage, err := img.ToImage(\"id\") // could not find 'blobs/sha256/...' in parsed layers\n\n# after: validate manifest paths against tar members first\ntar -tf img.tar   # confirm each manifest.json \"Layers\" entry is a member\n# then re-save if missing:\ndocker save myimg:latest -o img.tar","handlingStrategy":"validation","validationCode":"// Verify every manifest Layers entry exists as a tar member before calling ToImage.\nfunc validateLayerPaths(archiveTar io.Reader) error {\n    tr := tar.NewReader(archiveTar)\n    members := map[string]bool{}\n    for {\n        h, err := tr.Next()\n        if err == io.EOF { break }\n        if err != nil { return err }\n        members[path.Clean(h.Name)] = true\n    }\n    m, err := readManifest(archiveTar) // parse manifest.json separately\n    if err != nil { return err }\n    for _, p := range m.LayerTarPaths {\n        if !members[path.Clean(p)] { return fmt.Errorf(\"manifest layer missing from archive: %s\", p) }\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":"if _, err := imgArchive.ToImage(id); err != nil {\n    if strings.Contains(err.Error(), \"in parsed layers\") {\n        // manifest/archive mismatch: re-save the image and rebuild the archive\n    }\n    return err\n}","preventionTips":["Never hand-edit manifest.json or shuffle layer blobs inside a save archive","Validate downloads (checksums) so truncated saves never reach the parser","Generate archives exclusively with 'docker save'/'podman image save'"],"tags":["manifest","layer-mismatch","docker-archive","dive","validation"],"backgroundTag":null,"analyzedSha":"d6c691947f8fda635c952a17ee3b7555379d58f0","analyzedAt":"2026-08-15T09:42:35.293Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}