lima-vm/lima · error

field `%s.proto` must be %#q, %#q, or %#q

Error message

field `%s.proto` must be %#q, %#q, or %#q

What it means

Lima's limayaml.Validate() rejects a port-forwarding rule whose `proto` field is not one of the three allowed constants: ProtoTCP ("tcp"), ProtoUDP ("udp"), or ProtoAny ("any"). The switch at validate.go:376-379 falls through to default for any other value. The error is joined into the aggregate validation error list produced by `limactl validate`, `limactl edit`, `limactl start` (via templateArgs/applyYQ), instance restart, and clone/rename.

Source

Thrown at pkg/limayaml/validate.go:379

			if !filepath.IsAbs(rule.HostSocket) {
				// should be unreachable because FillDefault() will prepend the instance directory to relative names
				errs = errors.Join(errs, fmt.Errorf("field `%s.hostSocket` must be an absolute path, but is %#q", field, rule.HostSocket))
			}
			if rule.GuestSocket == "" && rule.GuestPortRange[1]-rule.GuestPortRange[0] > 0 {
				errs = errors.Join(errs, fmt.Errorf("field `%s.hostSocket` can only be mapped from a single port or socket. not a range", field))
			}
		} else if rule.GuestPortRange[1]-rule.GuestPortRange[0] != rule.HostPortRange[1]-rule.HostPortRange[0] {
			errs = errors.Join(errs, fmt.Errorf("field `%s.hostPortRange` must specify the same number of ports as field `%s.guestPortRange`", field, field))
		}

		if len(rule.HostSocket) >= osutil.UnixPathMax {
			errs = errors.Join(errs, fmt.Errorf("field `%s.hostSocket` must be less than UNIX_PATH_MAX=%d characters, but is %d",
				field, osutil.UnixPathMax, len(rule.HostSocket)))
		}
		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 != "" {

View on GitHub (pinned to dd909d0973)

Solutions

  1. Set the rule's proto to one of exactly: tcp, udp, or any (lowercase).
  2. If the rule targets both protocols, use `any` rather than omitting or inventing a value.
  3. Run `limactl validate <file>` before `start`/`edit` to catch the typo early.

Example fix

# before
portForwards:
  - guestPort: 80
    hostPort: 8080
    proto: "TCP"
# after
portForwards:
  - guestPort: 80
    hostPort: 8080
    proto: "tcp"
Defensive patterns

Strategy: validation

Validate before calling

allowed := map[string]bool{"tcp": true, "udp": true, "any": true}
for i, r := range cfg.PortForwards {
    if !allowed[r.Proto] {
        return fmt.Errorf("PortForwards[%d].proto %q invalid; use tcp, udp, or any", i, r.Proto)
    }
}

Type guard

func validProto(p string) bool { return p == "tcp" || p == "udp" || p == "any" }

Try / catch

err := limayaml.Validate(y, "strict")
if err != nil {
    var fieldErr *ValidationError
    if errors.As(err, &fieldErr) { /* inspect joined field errors */ }
    return fmt.Errorf("lima config invalid: %w", err)
}

Prevention

When it happens

Trigger: Any code path calling limayaml.Validate() on a LimaYAML whose PortForwards[i].Proto is an unlisted string (e.g. "TCP", "sctp", "", "icmp") — specifically: limactl validate/templateValidateAction, limactl edit (editAction), restartAction, applyYQExpressionToExistingInstance, cloneOrRenameAction, or templateArgs evaluation.

Common situations: Typo or wrong-casing `proto: "TCP"` (values are lowercase), copying a rule from a different tool that accepts other protocols, YAML editing that leaves proto empty in a hand-written rule, or programmatic generation writing an invalid enum.

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/e05d9c6cd5ad4cb1. Report an issue: GitHub.