lima-vm/lima · error
field `mounts[%d].location` refers to an unexpandable path:
Error message
field `mounts[%d].location` refers to an unexpandable path: %#q: %w
What it means
After the absolute-path check, Validate() expands `~` in `mounts[i].location` using localpathutil.Expand. If expansion fails (e.g. malformed `~user` syntax or unresolvable user), this error wraps the Expand error.
Source
Thrown at pkg/limayaml/validate.go:132
if _, err := units.RAMInBytes(*y.Disk); err != nil {
errs = errors.Join(errs, fmt.Errorf("field `disk` has an invalid value: %w", err))
}
for i, disk := range y.AdditionalDisks {
if err := identifiers.Validate(disk.Name); err != nil {
errs = errors.Join(errs, fmt.Errorf("field `additionalDisks[%d].name is invalid`: %w", i, err))
}
}
for i, f := range y.Mounts {
if !filepath.IsAbs(f.Location) && !strings.HasPrefix(f.Location, "~") {
errs = errors.Join(errs, fmt.Errorf("field `mounts[%d].location` must be an absolute path, got %#q",
i, f.Location))
}
// f.Location has already been expanded in FillDefaults(), but that function cannot return errors.
loc, err := localpathutil.Expand(f.Location)
if err != nil {
errs = errors.Join(errs, fmt.Errorf("field `mounts[%d].location` refers to an unexpandable path: %#q: %w", i, f.Location, err))
}
st, err := os.Stat(loc)
if err != nil {
if !errors.Is(err, os.ErrNotExist) {
errs = errors.Join(errs, fmt.Errorf("field `mounts[%d].location` refers to an inaccessible path: %#q: %w", i, f.Location, err))
}
if warn {
logrus.Warnf("field `mounts[%d].location` refers to a non-existent directory: %#q:", i, f.Location)
}
} else if !st.IsDir() {
errs = errors.Join(errs, fmt.Errorf("field `mounts[%d].location` refers to a non-directory path: %#q: %w", i, f.Location, err))
}
switch *f.MountPoint {
case "/", "/bin", "/dev", "/etc", "/home", "/opt", "/sbin", "/tmp", "/usr", "/var":
errs = errors.Join(errs, fmt.Errorf("field `mounts[%d].mountPoint` must not be a system path such as /etc or /usr", i))
// home directory defined in "cidata.iso:/user-data"
case *y.User.Home:View on GitHub (pinned to dd909d0973)
Solutions
- Replace the unexpandable tilde path with a fully absolute path like `/home/otheruser/data`
- Verify the target user/home exists on the host (`echo ~otheruser` in shell)
- Re-validate the template after fixing
Example fix
// before mounts: - location: ~nonexistentuser/data // after mounts: - location: /home/realuser/data
Defensive patterns
Strategy: validation
Validate before calling
const { execSync } = require('child_process')
function expandableTilde(loc) {
if (!loc.startsWith('~')) return true
try { execSync(`echo ${loc}`, { shell: '/bin/sh', stdio: 'ignore' }); return true } catch { return false }
} Type guard
function isExpandableHomePath(p) { return typeof p === 'string' && (p === '~' || /^~(\/|$)/.test(p)) } // avoid ~user forms unless the user exists Try / catch
try { await limactl(['template','validate', file]) } catch (e) { if (/unexpandable path/.test(e.message)) { replaceTildeWithAbsolute(); } else throw e } Prevention
- Avoid `~user/...` forms; hardcode /home/user when needed
- Test configs on the actual host user where HOME is set
- Prefer absolute paths for cross-machine templates
When it happens
Trigger: A mount location contains a tilde form that localpathutil.Expand cannot resolve, such as `~nonexistentuser/x`, when Validate runs.
Common situations: Typing `~otheruser/data` where that user does not exist on the host; malformed paths like `~` followed by unexpected characters; HOME unset in odd environments.
Related errors
- field `mounts[%d].location` must be an absolute path, got %#
- field `mounts[%d].location` refers to a non-directory path:
- field `mounts[%d].mountPoint` must not be a system path such
- field `mounts[%d].mountPoint` is the reserved internal home
- field `mounts[%d].mountPoint` must not start with `~`
AI-assisted analysis of lima-vm/lima@dd909d0973 (2026-09-01).
Data as JSON: /api/errors/1f564a3f60107cc3.
Report an issue: GitHub.