wagoodman/dive · critical
failed to unmarshal docker config: %w
Error message
failed to unmarshal docker config: %w
What it means
Raised as a panic by newConfig (dive/image/docker/config.go:31) when json.Unmarshal of the image's config JSON fails. The config blob is the <id>.json file from a docker archive (or the OCI config); any structural mismatch between the blob and the config struct aborts analysis with 'failed to unmarshal docker config: %w'.
Source
Thrown at dive/image/docker/config.go:31
type rootFs struct {
Type string `json:"type"`
DiffIds []string `json:"diff_ids"`
}
type historyEntry struct {
ID string
Size uint64
Created string `json:"created"`
Author string `json:"author"`
CreatedBy string `json:"created_by"`
EmptyLayer bool `json:"empty_layer"`
}
func newConfig(configBytes []byte) config {
var imageConfig config
err := json.Unmarshal(configBytes, &imageConfig)
if err != nil {
panic(fmt.Errorf("failed to unmarshal docker config: %w", err))
}
layerIdx := 0
for idx := range imageConfig.History {
if imageConfig.History[idx].EmptyLayer {
imageConfig.History[idx].ID = "<missing>"
} else {
imageConfig.History[idx].ID = imageConfig.RootFs.DiffIds[layerIdx]
layerIdx++
}
}
return imageConfig
}
func isConfig(configBytes []byte) bool {
var imageConfig config
err := json.Unmarshal(configBytes, &imageConfig)View on GitHub (pinned to d6c691947f)
Solutions
- Verify the archive loads with docker itself: docker load -i image.tar - if that also fails, re-save the image
- Re-pull and re-save from the source registry to eliminate truncation/corruption
- Inspect the config: tar -xf image.tar -O <manifest-listed-config>.json | jq . and compare against a known-good image's config
- Update dive - config-struct fixes for newer schema variants land regularly
Example fix
# before # (archive from an interrupted download) dive docker-archive://image.tar # panics: failed to unmarshal docker config # after # verify + regenerate the archive docker pull app:tag && docker save -o image.tar app:tag dive docker-archive://image.tar
Defensive patterns
Strategy: validation
Validate before calling
// validate the archive's config JSON before handing it to dive
cfgName, err := configNameFromManifest(tarPath) // read manifest.json -> .[0].Config
if err != nil {
return fmt.Errorf("archive lacks a parseable manifest.json: %w", err)
}
raw, err := fileFromTar(tarPath, cfgName)
if err != nil {
return fmt.Errorf("config %s missing from archive", cfgName)
}
var probe map[string]json.RawMessage
if err := json.Unmarshal(raw, &probe); err != nil {
return fmt.Errorf("config JSON is corrupt: %w", err)
} Try / catch
// newConfig panics; contain it if you call the image_archive package directly:
func safeNewConfig(b []byte) (c docker.Config, err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("invalid image config: %v", r)
}
}()
// ... construct via the package's exported path
return c, nil
} Prevention
- Always verify an archive with 'docker load -i' before dive
- Never post-process/repack archives between save and analysis
- Pin image build/save tooling so config schema changes are caught in CI
When it happens
Trigger: Feeding a docker-archive whose config JSON is corrupted or truncated (partial docker save, interrupted download); images produced by tools whose config JSON deviates from Docker's schema (unexpected types for history, rootfs.diff_ids, created/author fields); a tar whose *.json file is picked up as the config but is actually something else.
Common situations: Registries/builders that emit slightly different config schemas (older `created` as non-string, extra nesting), hand-assembled or edited archives, and partial downloads - docker itself may tolerate variants that this strict Unmarshal does not.
Related errors
- failed to unmarshal manifest: %w
- could not find image config
- cannot marshal export payload: %w
- cannot open export file: %w
- cannot write to export file: %w
AI-assisted analysis of wagoodman/dive@d6c691947f (2026-08-15).
Data as JSON: /api/errors/3d8f83b419807101.
Report an issue: GitHub.