{"record":{"id":"8cf30ac3df48be21","repo":"wagoodman/dive","slug":"unexpected-tar-file-xheader-type-v-name-s","errorCode":null,"errorMessage":"unexpected tar file (XHeader): type=%v name=%s","messagePattern":"unexpected tar file \\(XHeader\\): type=(.+?) name=(.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"dive/image/docker/image_archive.go","lineNumber":242,"sourceCode":"\tfor {\n\t\theader, err := tarReader.Next()\n\t\tif err == io.EOF {\n\t\t\tbreak\n\t\t} else if err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\t// always ensure relative path notations are not parsed as part of the filename\n\t\tname := path.Clean(header.Name)\n\t\tif name == \".\" {\n\t\t\tcontinue\n\t\t}\n\n\t\tswitch header.Typeflag {\n\t\tcase tar.TypeXGlobalHeader:\n\t\t\treturn nil, fmt.Errorf(\"unexpected tar file: (XGlobalHeader): type=%v name=%s\", header.Typeflag, name)\n\t\tcase tar.TypeXHeader:\n\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)","sourceCodeStart":224,"sourceCodeEnd":260,"githubUrl":"https://github.com/wagoodman/dive/blob/d6c691947f8fda635c952a17ee3b7555379d58f0/dive/image/docker/image_archive.go#L224-L260","documentation":"This error is returned by dive's docker image archive parser when it encounters a tar entry whose typeflag is tar.TypeXHeader ('x'), a PAX extended header. PAX headers carry metadata (long paths, large UIDs/GIDs, sparse files, timestamps) that applies to the entry that follows it, and dive's parser has no handling for them, so it aborts the whole archive walk instead of skipping or merging the extension data.","triggerScenarios":"Calling NewImageArchive (or any dive API that ingests a 'docker save' tar, e.g. podman resolver.Fetch -> resolveFromDockerArchive) on an archive containing PAX-format entries. This happens when the tar was produced by bsdtar (macOS default), by buildkit-built images exported with newer docker/podman versions, or by any tool that writes POSIX.1-2001 (pax) format tars.","commonSituations":"Running dive (or a tool embedding it) on an image saved on macOS where the layer tars were re-packed with PAX headers; images built with buildkit and saved with recent Docker/Podman releases; hand-modified or re-tarred image archives where the default tar format is pax.","solutions":["Re-export the image so layer tars use ustar/gnu format: 'docker save <image> -o img.tar' directly (do not untar/re-tar on macOS with default bsdtar settings)","If re-tar locally, force a compatible format: 'tar --format=gnu -cf out.tar ...' (GNU tar) or 'COPYFILE_DISABLE=1 tar --format=ustar ...' on macOS","Upgrade dive to a release that tolerates or skips PAX extension headers instead of erroring","If you control the archive producer, emit POSIX ustar archives (no --format=pax, no --xattrs)"],"exampleFix":"# before (macOS bsdtar defaults can inject PAX headers)\nuntar docker-save.tar && tar -cf rebuilt.tar .   # may produce typeflag 'x'\ndive docker-save.tar\n\n# after (force gnu/ustar, drop extended headers)\nuntar docker-save.tar && tar --format=gnu -cf rebuilt.tar .\ndive rebuilt.tar","handlingStrategy":"validation","validationCode":"// Before loading, scan the archive for PAX extension headers and reject/pre-process it.\nfunc hasPaxHeaders(r io.Reader) (bool, error) {\n    tr := tar.NewReader(r)\n    for {\n        hdr, err := tr.Next()\n        if err == io.EOF { return false, nil }\n        if err != nil { return false, err }\n        if hdr.Typeflag == tar.TypeXHeader || hdr.Typeflag == tar.TypeXGlobalHeader {\n            return true, nil\n        }\n    }\n}\n\npax, err := hasPaxHeaders(bytes.NewReader(archiveBytes))\nif err == nil && pax { /* re-pack with gnu format or reject with a clear message */ }","typeGuard":null,"tryCatchPattern":"// Go: treat as a returned error and branch on the message.\nif _, err := docker.NewImageArchive(f); err != nil {\n    if strings.Contains(err.Error(), \"unexpected tar file\") {\n        // archive format problem: re-export with ustar/gnu format and retry once\n    }\n    return err\n}","preventionTips":["Always consume the tar produced directly by 'docker save'/'podman image save' instead of re-tarring it","When repacking layer tars, force '--format=gnu' or '--format=ustar' and disable xattrs/POSIX.1-2001 extensions","Avoid macOS bsdtar defaults when touching docker-save archives; set COPYFILE_DISABLE=1 as well"],"tags":["tar","pax","docker-archive","dive","image-parsing"],"backgroundTag":null,"analyzedSha":"d6c691947f8fda635c952a17ee3b7555379d58f0","analyzedAt":"2026-08-15T09:42:35.293Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}