lima-vm/lima · error

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

Error message

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

What it means

Validate() requires CopyToHost rules to use an absolute guest-side path: if GuestFile is non-empty it must satisfy path.IsAbs() (POSIX semantics, validate.go:393-395). CopyToHost copies files from inside the VM to the host, so the guest path is interpreted in the guest filesystem and must be fully qualified (e.g. /etc/hosts).

Source

Thrown at pkg/limayaml/validate.go:394

		switch rule.Proto {
		case limatype.ProtoTCP, limatype.ProtoUDP, limatype.ProtoAny:
		default:
			errs = errors.Join(errs, fmt.Errorf("field `%s.proto` must be %#q, %#q, or %#q", field, limatype.ProtoTCP, limatype.ProtoUDP, limatype.ProtoAny))
		}
		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"))
		}
	}

View on GitHub (pinned to dd909d0973)

Solutions

  1. Prefix the guest path with "/" — use a fully qualified path like /etc/hosts.
  2. Do not use ~ or environment variables for GuestFile; the guest path must be literal and absolute.
  3. Run `limactl validate <file>` to catch path issues before starting the instance.

Example fix

# before
copyToHost:
  - guest: etc/hosts
    host: /tmp/hosts
# after
copyToHost:
  - guest: /etc/hosts
    host: /tmp/hosts
Defensive patterns

Strategy: validation

Validate before calling

for i, r := range cfg.CopyToHost {
    if r.GuestFile != "" && !strings.HasPrefix(r.GuestFile, "/") {
        return fmt.Errorf("CopyToHost[%d].guest must be absolute: %q", i, r.GuestFile)
    }
}

Type guard

func absGuestPath(p string) bool { return p != "" && path.IsAbs(p) }

Try / catch

if err := limayaml.Validate(y, "strict"); err != nil {
    return fmt.Errorf("copyToHost guest path must start with /: %w", err)
}

Prevention

When it happens

Trigger: limayaml.Validate() (via limactl validate/edit/start/restart/clone/rename/template tooling) sees CopyToHost[i].GuestFile set to a relative path like "etc/hosts" or "~/notes".

Common situations: Hand-writing a copyToHost rule with a relative path; assuming ~ expansion applies on the guest side; copying host-style paths into the guest field.

Related errors


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