lima-vm/lima · error

field `probe[%d].script` must start with a '#!' line

Error message

field `probe[%d].script` must start with a '#!' line

What it means

Every probe script must be an executable script beginning with a shebang (`#!`) line, because it is written to a file and executed inside the guest. Validate() rejects probe.script values that do not start with `#!`.

Source

Thrown at pkg/limayaml/validate.go:299

		}
		for i, f := range y.Containerd.Archives {
			err := validateFileObject(f, fmt.Sprintf("containerd.archives[%d]", i))
			if err != nil {
				errs = errors.Join(errs, err)
			}
		}
	}
	for i, p := range y.Probes {
		if p.File != nil {
			if p.File.URL != "" {
				errs = errors.Join(errs, fmt.Errorf("field `probe[%d].file.url` must be empty during validation (script should already be embedded)", i))
			}
			if p.File.Digest != nil {
				errs = errors.Join(errs, fmt.Errorf("field `probe[%d].file.digest` support is not yet implemented", i))
			}
		}
		if p.Script != nil && !strings.HasPrefix(*p.Script, "#!") {
			errs = errors.Join(errs, fmt.Errorf("field `probe[%d].script` must start with a '#!' line", i))
		}
		switch p.Mode {
		case limatype.ProbeModeReadiness:
		default:
			errs = errors.Join(errs, fmt.Errorf("field `probe[%d].mode` can only be %#q", i, limatype.ProbeModeReadiness))
		}
	}
	for i, rule := range y.PortForwards {
		field := fmt.Sprintf("portForwards[%d]", i)
		if *rule.GuestIPMustBeZero && !rule.GuestIP.Equal(net.IPv4zero) {
			errs = errors.Join(errs, fmt.Errorf("field `%s.guestIPMustBeZero` can only be true when field `%s.guestIP` is 0.0.0.0", field, field))
		}
		if rule.GuestPort != 0 {
			if rule.GuestSocket != "" {
				errs = errors.Join(errs, fmt.Errorf("field `%s.guestPort` must be 0 when field `%s.guestSocket` is set", field, field))
			}
			if rule.GuestPort != rule.GuestPortRange[0] {
				errs = errors.Join(errs, fmt.Errorf("field `%s.guestPort` must match field `%s.guestPortRange[0]`", field, field))

View on GitHub (pinned to dd909d0973)

Solutions

  1. Add a shebang line as the very first line, e.g. `#!/bin/sh`
  2. Remove leading blank lines in the script block (use `|-` if trailing newline matters)
  3. If only a probe name/command is intended, check probe.mode/hint fields instead of a script

Example fix

// before
probes:
- script: |
    systemctl is-active docker
// after
probes:
- script: |
    #!/bin/sh
    systemctl is-active docker
Defensive patterns

Strategy: validation

Validate before calling

for i, p := range cfg.Probes {
    if p.Script != nil && !strings.HasPrefix(*p.Script, "#!") {
        return fmt.Errorf("probe[%d].script must start with '#!'", i)
    }
}

Type guard

func probeScriptIsExecutable(p limatype.Probe) bool {
    return p.Script != nil && strings.HasPrefix(*p.Script, "#!")
}

Prevention

When it happens

Trigger: Calling limactl validate/start/etc. with a probe whose `script:` begins with blank lines, comments, or commands instead of `#!` on the first line.

Common situations: Writing `script: |` blocks starting with shell commands (`curl ...`) forgetting the shebang; leading empty line introduced by YAML block scalar formatting (`|` vs `|-` and indentation).

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


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