wagoodman/dive · error
could not find image manifest
Error message
could not find image manifest
What it means
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'.
Source
Thrown at dive/image/docker/image_archive.go:177
}
}
}
manifestContent, exists := jsonFiles["manifest.json"]
if exists {
img.manifest = newManifest(manifestContent)
} else {
// manifest.json is not part of the OCI spec, docker includes it for compatibility
// Provide compatibility by finding the config and using our layerMap
var configPath string
for path, content := range jsonFiles {
if isConfig(content) {
configPath = path
break
}
}
if len(configPath) == 0 {
return img, fmt.Errorf("could not find image manifest")
}
var layerPaths []string
for k := range img.layerMap {
layerPaths = append(layerPaths, k)
}
img.manifest = manifest{
ConfigPath: configPath,
LayerTarPaths: layerPaths,
}
}
configContent, exists := jsonFiles[img.manifest.ConfigPath]
if !exists {
return img, fmt.Errorf("could not find image config")
}
img.config = newConfig(configContent)View on GitHub (pinned to d6c691947f)
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
Example fix
# before (OCI-layout tar from kaniko) dive docker-archive://app-oci.tar # could not find image manifest # after skopeo copy oci-archive:app-oci.tar docker-archive:app.tar:app:ci dive docker-archive://app.tar
Defensive patterns
Strategy: fallback
Validate before calling
// detect OCI-only archives before analysis
func hasDockerManifest(tarPath string) bool {
f, err := os.Open(tarPath)
if err != nil {
return false
}
defer f.Close()
tr := tar.NewReader(f)
for {
hdr, err := tr.Next()
if err != nil {
return false
}
if filepath.Base(hdr.Name) == "manifest.json" {
return true
}
}
}
if !hasDockerManifest(p) {
// convert or go through an engine instead of failing later
} Try / catch
img, err := resolver.Fetch(ctx, "docker-archive://"+p)
if err != nil && strings.Contains(err.Error(), "could not find image manifest") {
// fallback: convert OCI -> docker archive with skopeo
_ = exec.Command("skopeo", "copy", "oci-archive:"+p, "docker-archive:"+p+".docker.tar:tmp:ci").Run()
img, err = resolver2.Fetch(ctx, "docker-archive://"+p+".docker.tar")
}
if err != nil {
return err
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- could not find image config
- could not find '%s' in parsed layers
- failed to unmarshal manifest: %w
- build option not supported for docker archive resolver
- not implemented
AI-assisted analysis of wagoodman/dive@d6c691947f (2026-08-15).
Data as JSON: /api/errors/f66bbcd814d83144.
Report an issue: GitHub.