{"record":{"id":"4e165ac6c3804d34","repo":"dagger/dagger","slug":"cannot-determine-upperdir-from-mount-option","errorCode":null,"errorMessage":"cannot determine upperdir from mount option","messagePattern":"cannot determine upperdir from mount option","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"engine/snapshots/fsdiff/overlay_linux.go","lineNumber":69,"sourceCode":"\t\tupperlayers, err := GetOverlayLayers(upperM)\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\n\t\tif len(upperlayers) != len(lowerlayers)+1 {\n\t\t\treturn \"\", errors.Errorf(\"cannot determine diff of more than one upper directories\")\n\t\t}\n\t\tfor i := 0; i < len(lowerlayers); i++ {\n\t\t\tif upperlayers[i] != lowerlayers[i] {\n\t\t\t\treturn \"\", errors.Errorf(\"layer %d must be common between upper and lower snapshots\", i)\n\t\t\t}\n\t\t}\n\t\tupperdir = upperlayers[len(upperlayers)-1]\n\t} else {\n\t\treturn \"\", errors.Errorf(\"multiple mount configurations are not supported\")\n\t}\n\tif upperdir == \"\" {\n\t\treturn \"\", errors.Errorf(\"cannot determine upperdir from mount option\")\n\t}\n\treturn upperdir, nil\n}\n\nfunc GetOverlayLayers(m mount.Mount) ([]string, error) {\n\tvar u string\n\tvar uFound bool\n\tvar l []string\n\tfor _, o := range m.Options {\n\t\tif strings.HasPrefix(o, \"upperdir=\") {\n\t\t\tu, uFound = strings.TrimPrefix(o, \"upperdir=\"), true\n\t\t} else if strings.HasPrefix(o, \"lowerdir=\") {\n\t\t\tl = strings.Split(strings.TrimPrefix(o, \"lowerdir=\"), \":\")\n\t\t\tfor i, j := 0, len(l)-1; i < j; i, j = i+1, j-1 {\n\t\t\t\tl[i], l[j] = l[j], l[i]\n\t\t\t}\n\t\t} else if strings.HasPrefix(o, \"workdir=\") || o == \"index=off\" || o == \"userxattr\" || strings.HasPrefix(o, \"redirect_dir=\") || o == \"volatile\" {\n\t\t\tcontinue","sourceCodeStart":51,"sourceCodeEnd":87,"githubUrl":"https://github.com/dagger/dagger/blob/82ba2681dbe30d3547a1dc50ea495900ab5b6047/engine/snapshots/fsdiff/overlay_linux.go#L51-L87","documentation":"After selecting the mount configuration, GetUpperdir extracts the final layer of the upper overlay stack as the upperdir. This error fires when that value ends up empty — i.e. the upper overlay mount parsed successfully but yielded no layers or no upperdir= option, so the code cannot name the directory holding the writable changes.","triggerScenarios":"The upper mount's Options contain no upperdir= entry and its lowerdir list is empty (GetOverlayLayers returns nil), making upperlayers[len-1] unreachable/empty — e.g. a read-only overlay mount with only lowerdir, an options list with only ignorable options (workdir=/index=off/...), or a malformed mount produced by the snapshotter.","commonSituations":"Diffing a read-only overlay view (no upperdir by design); a snapshotter that mounts overlay without an upperdir option; an options list that lost its upperdir due to a mount-helper or logging wrapper rewriting options; passing a lower-style overlay mount as the upper mount by mistake.","solutions":["Ensure the upper mount is a writable overlay mount whose Options include an upperdir= entry","Verify you are passing the upper snapshot's mount (with upperdir), not a lower/read-only view, as the upper argument","Inspect m.Options of the upper mount before calling and confirm upperdir=/lowerdir= are present","Fix or update the snapshotter/mount helper that is dropping the upperdir option"],"exampleFix":"// before\nupperM := mount.Mount{Type: \"overlay\", Source: \"overlay\", Options: []string{\"lowerdir=/a:/b\"}} // read-only\n// after\nupperM := mount.Mount{Type: \"overlay\", Source: \"overlay\", Options: []string{\"lowerdir=/a:/b\", \"upperdir=/c\", \"workdir=/w\"}}","handlingStrategy":"validation","validationCode":"func hasUpperdir(m mount.Mount) bool {\n    for _, o := range m.Options {\n        if strings.HasPrefix(o, \"upperdir=\") && len(o) > len(\"upperdir=\") {\n            return true\n        }\n    }\n    return false\n}\n// call before diffing:\nif !hasUpperdir(upperM) { return fmt.Errorf(\"upper mount has no upperdir; it is read-only or malformed\") }","typeGuard":"func isWritableOverlayMount(m mount.Mount) bool {\n    if m.Type != \"overlay\" { return false }\n    for _, o := range m.Options {\n        if strings.HasPrefix(o, \"upperdir=\") { return true }\n    }\n    return false\n}","tryCatchPattern":null,"preventionTips":["Confirm the upper snapshot is mounted writable before diffing","Don't pass read-only overlay views as the upper mount","Validate mount options with a hasUpperdir check first"],"tags":["overlayfs","linux","mount-options","snapshot"],"backgroundTag":"missing-overlay-upperdir","analyzedSha":"82ba2681dbe30d3547a1dc50ea495900ab5b6047","analyzedAt":"2026-09-05T07:21:37.930Z","contentChangedAt":"2026-09-05T07:21:37.930Z","schemaVersion":2},"datasetVersion":"2026-09-12T12:17:11.808Z"}