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
- Add a shebang line as the very first line, e.g. `#!/bin/sh`
- Remove leading blank lines in the script block (use `|-` if trailing newline matters)
- 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
- Always begin probe scripts with #!/bin/sh or #!/bin/bash
- Use YAML `|-` blocks and check the first rendered line has no leading blank
- Lint lima.yaml so probe scripts are checked before limactl start
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
- field `user.shell` must be one of %v for Windows guest, got
- field `probe[%d].file.url` must be empty during validation (
- field `probe[%d].file.digest` support is not yet implemented
- unsupported shell %#q for Windows guest, must be one of %v
- errors inspecting instance: %+v
AI-assisted analysis of lima-vm/lima@dd909d0973 (2026-09-01).
Data as JSON: /api/errors/f5aaccc95dc486cf.
Report an issue: GitHub.