lima-vm/lima · error

field `%s.host` must be an absolute path, but is %#q

Error message

field `%s.host` must be an absolute path, but is %#q

What it means

Validate() requires CopyToHost rules to use an absolute host-side path: if HostFile is non-empty it must satisfy filepath.IsAbs() (host OS semantics, validate.go:398-400). The destination on the host machine must be a fully qualified path so the copy target is unambiguous.

Source

Thrown at pkg/limayaml/validate.go:399

		if rule.Reverse && rule.GuestSocket == "" {
			errs = errors.Join(errs, fmt.Errorf("field `%s.reverse` must be %t", field, false))
		}
		if rule.Reverse && rule.HostSocket == "" {
			errs = errors.Join(errs, fmt.Errorf("field `%s.reverse` must be %t", field, false))
		}
		// Not validating that the various GuestPortRanges and HostPortRanges are not overlapping. Rules will be
		// processed sequentially and the first matching rule for a guest port determines forwarding behavior.
	}
	for i, rule := range y.CopyToHost {
		field := fmt.Sprintf("CopyToHost[%d]", i)
		if rule.GuestFile != "" {
			if !path.IsAbs(rule.GuestFile) {
				errs = errors.Join(errs, fmt.Errorf("field `%s.guest` must be an absolute path, but is %#q", field, rule.GuestFile))
			}
		}
		if rule.HostFile != "" {
			if !filepath.IsAbs(rule.HostFile) {
				errs = errors.Join(errs, fmt.Errorf("field `%s.host` must be an absolute path, but is %#q", field, rule.HostFile))
			}
		}
	}

	if y.VMType != nil && *y.VMType == limatype.HCS {
		if y.HostResolver.Enabled != nil && *y.HostResolver.Enabled && len(y.DNS) > 0 {
			errs = errors.Join(errs, errors.New("field `dns` must be empty when field `HostResolver.Enabled` is true"))
		}
	} else {
		if HostResolverEnabled(y) && len(y.DNS) > 0 {
			errs = errors.Join(errs, errors.New("field `dns` must be empty when field `HostResolver.Enabled` is true"))
		}
	}

	err := validateNetwork(y)
	if err != nil {
		errs = errors.Join(errs, err)
	}

View on GitHub (pinned to dd909d0973)

Solutions

  1. Use an absolute host path (e.g. /home/user/copy.txt); expand ~ yourself in the template.
  2. On the host side, filepath.IsAbs is platform-specific — on Windows use a drive-qualified path (C:\...).
  3. Pre-validate with `limactl validate` before limactl start.

Example fix

# before
copyToHost:
  - guest: /var/log/app.log
    host: logs/app.log
# after
copyToHost:
  - guest: /var/log/app.log
    host: /home/me/logs/app.log
Defensive patterns

Strategy: validation

Validate before calling

for i, r := range cfg.CopyToHost {
    if r.HostFile != "" && !filepath.IsAbs(r.HostFile) {
        return fmt.Errorf("CopyToHost[%d].host must be absolute: %q", i, r.HostFile)
    }
}

Type guard

func absHostPath(p string) bool { return p != "" && filepath.IsAbs(p) }

Try / catch

if err := limayaml.Validate(y, "strict"); err != nil {
    return fmt.Errorf("copyToHost host path must be absolute: %w", err)
}

Prevention

When it happens

Trigger: limayaml.Validate() (via limactl validate/edit/start/restart/clone/rename/template tooling) sees CopyToHost[i].HostFile set to a relative path like "out.txt" or "./tmp/x".

Common situations: Writing `host: ~/copy.txt` expecting shell tilde expansion; template reuse across machines where a relative destination seemed convenient; programmatically generated rules defaulting to relative output names.

Related errors


AI-assisted analysis of lima-vm/lima@dd909d0973 (2026-09-01). Data as JSON: /api/errors/4753fd27454690f9. Report an issue: GitHub.