{"record":{"id":"3d8f83b419807101","repo":"wagoodman/dive","slug":"failed-to-unmarshal-docker-config-w","errorCode":null,"errorMessage":"failed to unmarshal docker config: %w","messagePattern":"failed to unmarshal docker config: %w","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"dive/image/docker/config.go","lineNumber":31,"sourceCode":"type rootFs struct {\n\tType    string   `json:\"type\"`\n\tDiffIds []string `json:\"diff_ids\"`\n}\n\ntype historyEntry struct {\n\tID         string\n\tSize       uint64\n\tCreated    string `json:\"created\"`\n\tAuthor     string `json:\"author\"`\n\tCreatedBy  string `json:\"created_by\"`\n\tEmptyLayer bool   `json:\"empty_layer\"`\n}\n\nfunc newConfig(configBytes []byte) config {\n\tvar imageConfig config\n\terr := json.Unmarshal(configBytes, &imageConfig)\n\tif err != nil {\n\t\tpanic(fmt.Errorf(\"failed to unmarshal docker config: %w\", err))\n\t}\n\n\tlayerIdx := 0\n\tfor idx := range imageConfig.History {\n\t\tif imageConfig.History[idx].EmptyLayer {\n\t\t\timageConfig.History[idx].ID = \"<missing>\"\n\t\t} else {\n\t\t\timageConfig.History[idx].ID = imageConfig.RootFs.DiffIds[layerIdx]\n\t\t\tlayerIdx++\n\t\t}\n\t}\n\n\treturn imageConfig\n}\n\nfunc isConfig(configBytes []byte) bool {\n\tvar imageConfig config\n\terr := json.Unmarshal(configBytes, &imageConfig)","sourceCodeStart":13,"sourceCodeEnd":49,"githubUrl":"https://github.com/wagoodman/dive/blob/d6c691947f8fda635c952a17ee3b7555379d58f0/dive/image/docker/config.go#L13-L49","documentation":"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'.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"# before\n# (archive from an interrupted download)\ndive docker-archive://image.tar  # panics: failed to unmarshal docker config\n\n# after\n# verify + regenerate the archive\ndocker pull app:tag && docker save -o image.tar app:tag\ndive docker-archive://image.tar","handlingStrategy":"validation","validationCode":"// validate the archive's config JSON before handing it to dive\ncfgName, err := configNameFromManifest(tarPath) // read manifest.json -> .[0].Config\nif err != nil {\n    return fmt.Errorf(\"archive lacks a parseable manifest.json: %w\", err)\n}\nraw, err := fileFromTar(tarPath, cfgName)\nif err != nil {\n    return fmt.Errorf(\"config %s missing from archive\", cfgName)\n}\nvar probe map[string]json.RawMessage\nif err := json.Unmarshal(raw, &probe); err != nil {\n    return fmt.Errorf(\"config JSON is corrupt: %w\", err)\n}","typeGuard":null,"tryCatchPattern":"// newConfig panics; contain it if you call the image_archive package directly:\nfunc safeNewConfig(b []byte) (c docker.Config, err error) {\n    defer func() {\n        if r := recover(); r != nil {\n            err = fmt.Errorf(\"invalid image config: %v\", r)\n        }\n    }()\n    // ... construct via the package's exported path\n    return c, nil\n}","preventionTips":["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"],"tags":["docker-archive","json","panic","corruption","schema-mismatch"],"backgroundTag":null,"analyzedSha":"d6c691947f8fda635c952a17ee3b7555379d58f0","analyzedAt":"2026-08-15T09:42:35.293Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}