dagger/dagger · error

cannot determine upperdir from mount option

Error message

cannot determine upperdir from mount option

What it means

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.

Source

Thrown at engine/snapshots/fsdiff/overlay_linux.go:69

		upperlayers, err := GetOverlayLayers(upperM)
		if err != nil {
			return "", err
		}

		if len(upperlayers) != len(lowerlayers)+1 {
			return "", errors.Errorf("cannot determine diff of more than one upper directories")
		}
		for i := 0; i < len(lowerlayers); i++ {
			if upperlayers[i] != lowerlayers[i] {
				return "", errors.Errorf("layer %d must be common between upper and lower snapshots", i)
			}
		}
		upperdir = upperlayers[len(upperlayers)-1]
	} else {
		return "", errors.Errorf("multiple mount configurations are not supported")
	}
	if upperdir == "" {
		return "", errors.Errorf("cannot determine upperdir from mount option")
	}
	return upperdir, nil
}

func GetOverlayLayers(m mount.Mount) ([]string, error) {
	var u string
	var uFound bool
	var l []string
	for _, o := range m.Options {
		if strings.HasPrefix(o, "upperdir=") {
			u, uFound = strings.TrimPrefix(o, "upperdir="), true
		} else if strings.HasPrefix(o, "lowerdir=") {
			l = strings.Split(strings.TrimPrefix(o, "lowerdir="), ":")
			for i, j := 0, len(l)-1; i < j; i, j = i+1, j-1 {
				l[i], l[j] = l[j], l[i]
			}
		} else if strings.HasPrefix(o, "workdir=") || o == "index=off" || o == "userxattr" || strings.HasPrefix(o, "redirect_dir=") || o == "volatile" {
			continue

View on GitHub (pinned to 82ba2681db)

Solutions

  1. Ensure the upper mount is a writable overlay mount whose Options include an upperdir= entry
  2. Verify you are passing the upper snapshot's mount (with upperdir), not a lower/read-only view, as the upper argument
  3. Inspect m.Options of the upper mount before calling and confirm upperdir=/lowerdir= are present
  4. Fix or update the snapshotter/mount helper that is dropping the upperdir option

Example fix

// before
upperM := mount.Mount{Type: "overlay", Source: "overlay", Options: []string{"lowerdir=/a:/b"}} // read-only
// after
upperM := mount.Mount{Type: "overlay", Source: "overlay", Options: []string{"lowerdir=/a:/b", "upperdir=/c", "workdir=/w"}}
Defensive patterns

Strategy: validation

Validate before calling

func hasUpperdir(m mount.Mount) bool {
    for _, o := range m.Options {
        if strings.HasPrefix(o, "upperdir=") && len(o) > len("upperdir=") {
            return true
        }
    }
    return false
}
// call before diffing:
if !hasUpperdir(upperM) { return fmt.Errorf("upper mount has no upperdir; it is read-only or malformed") }

Type guard

func isWritableOverlayMount(m mount.Mount) bool {
    if m.Type != "overlay" { return false }
    for _, o := range m.Options {
        if strings.HasPrefix(o, "upperdir=") { return true }
    }
    return false
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of dagger/dagger@82ba2681db (2026-09-05). Data as JSON: /api/errors/4e165ac6c3804d34. Report an issue: GitHub.