lima-vm/lima · error

field `provision[%d].playbook` refers to an inaccessible pat

Error message

field `provision[%d].playbook` refers to an inaccessible path: %#q: %w

What it means

When a provision entry has `playbook` set, Validate() stats the playbook path on the host to confirm it is readable. If os.Stat fails (missing file, typo, permission issue), this error is thrown wrapping the underlying os error. It only applies to entries where playbook is configured.

Source

Thrown at pkg/limayaml/validate.go:267

				errs = errors.Join(errs, fmt.Errorf("field `provision[%d].path` can only be set when mode is %#q, or %#q", i, limatype.ProvisionModeData, limatype.ProvisionModeYQ))
			}
			if p.Permissions != nil {
				errs = errors.Join(errs, fmt.Errorf("field `provision[%d].permissions` can only be set when mode is %#q, or %#q", i, limatype.ProvisionModeData, limatype.ProvisionModeYQ))
			}
			if p.Format != nil {
				errs = errors.Join(errs, fmt.Errorf("field `provision[%d].format` can only be set when mode is %#q", i, limatype.ProvisionModeYQ))
			}
		}
		if p.Playbook != "" {
			if p.Mode != limatype.ProvisionModeAnsible {
				errs = errors.Join(errs, fmt.Errorf("field `provision[%d].playbook can only be set when mode is %#q", i, limatype.ProvisionModeAnsible))
			}
			if p.Script != nil && *p.Script != "" {
				errs = errors.Join(errs, fmt.Errorf("field `provision[%d].script must be empty if playbook is set", i))
			}
			playbook := p.Playbook
			if _, err := os.Stat(playbook); err != nil {
				errs = errors.Join(errs, fmt.Errorf("field `provision[%d].playbook` refers to an inaccessible path: %#q: %w", i, playbook, err))
			}
			logrus.Warnf("provision mode %#q is deprecated, use `ansible-playbook %#q` instead", limatype.ProvisionModeAnsible, playbook)
		}
		if p.Script != nil {
			if strings.Contains(*p.Script, "LIMA_CIDATA") {
				logrus.Warn("provisioning scripts should not reference the LIMA_CIDATA variables")
			}
		}
	}
	needsContainerdArchives := (y.Containerd.User != nil && *y.Containerd.User) || (y.Containerd.System != nil && *y.Containerd.System)
	if needsContainerdArchives {
		if len(y.Containerd.Archives) == 0 {
			errs = errors.Join(errs, errors.New("field `containerd.archives` must be provided"))
		}
		for i, f := range y.Containerd.Archives {
			err := validateFileObject(f, fmt.Sprintf("containerd.archives[%d]", i))
			if err != nil {
				errs = errors.Join(errs, err)

View on GitHub (pinned to dd909d0973)

Solutions

  1. Fix the playbook path in lima.yaml to point to an existing file, relative to the lima.yaml location or absolute
  2. Run `ls` on the path as the same user running limactl to confirm access
  3. Replace the playbook provision with a plain `mode: dependency`+script or an `ansible-playbook` shell provision if no local playbook file exists

Example fix

// before
provision:
- mode: ansible
  playbook: playbooks/syte.yaml
// after
provision:
- mode: ansible
  playbook: playbooks/site.yaml
Defensive patterns

Strategy: validation

Validate before calling

for i, p := range cfg.Provision {
    if p.Playbook != "" {
        if _, err := os.Stat(p.Playbook); err != nil {
            return fmt.Errorf("provision[%d].playbook: %w", i, err)
        }
    }
}

Prevention

When it happens

Trigger: Calling limactl validate/start/edit/restart/apply with a provision[i].playbook path that does not exist, is a broken symlink, or is not readable by the current user.

Common situations: Relative path resolved from the wrong working directory; playbook file not committed or deleted; renamed playbook without updating lima.yaml; using a path inside the VM that only exists on the host (or vice versa).

Understand the failure class

Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.

Related errors


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