lima-vm/lima · error

field `provision[%d].script must be empty if playbook is set

Error message

field `provision[%d].script must be empty if playbook is set

What it means

Lima rejects provisioning entries that set both `playbook` and a non-empty `script`. A provision item with `playbook` must use mode `ansible` and carry no inline script, since the playbook itself is the provisioning payload. Validate() joins this error with any other config errors found.

Source

Thrown at pkg/limayaml/validate.go:263

			if p.Owner != nil {
				errs = errors.Join(errs, fmt.Errorf("field `provision[%d].owner` can only be set when mode is %#q", i, limatype.ProvisionModeData))
			}
			if p.Path != nil {
				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"))
		}

View on GitHub (pinned to dd909d0973)

Solutions

  1. Remove the `script:` field (or set it to empty) from the provision entry that has `playbook:` set
  2. Set `mode: ansible` on that provision entry, otherwise a second error is raised
  3. If both behaviors are needed, split into two provision entries: one with the script, one with the playbook

Example fix

// before
provision:
- mode: ansible
  playbook: site.yaml
  script: |
    echo hi
// after
provision:
- mode: ansible
  playbook: site.yaml
Defensive patterns

Strategy: validation

Validate before calling

for i, p := range cfg.Provision {
    if p.Playbook != "" && p.Script != nil && *p.Script != "" {
        return fmt.Errorf("provision[%d]: remove `script` when `playbook` is set", i)
    }
}

Type guard

func playbookWithScript(p limatype.Provision) bool {
    return p.Playbook != "" && p.Script != nil && *p.Script != ""
}

Prevention

When it happens

Trigger: Calling limactl validate / start / create / edit / restart / apply (all of which run limayaml.Validate) with a lima.yaml where a provision[i] entry has `playbook:` set AND a non-empty `script:` (or `script: |` block).

Common situations: Hand-editing lima.yaml to add an ansible playbook while leaving an older `script` block in the same provision item; converting a script-based provision to playbook-based and forgetting to delete the script; YAML merge snippets that accidentally reintroduce a script key.

Related errors


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