{"record":{"id":"e05d9c6cd5ad4cb1","repo":"lima-vm/lima","slug":"field-s-proto-must-be-q-q-or-q","errorCode":null,"errorMessage":"field `%s.proto` must be %#q, %#q, or %#q","messagePattern":"field `(.+?)\\.proto` must be %#q, %#q, or %#q","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/limayaml/validate.go","lineNumber":379,"sourceCode":"\t\t\tif !filepath.IsAbs(rule.HostSocket) {\n\t\t\t\t// should be unreachable because FillDefault() will prepend the instance directory to relative names\n\t\t\t\terrs = errors.Join(errs, fmt.Errorf(\"field `%s.hostSocket` must be an absolute path, but is %#q\", field, rule.HostSocket))\n\t\t\t}\n\t\t\tif rule.GuestSocket == \"\" && rule.GuestPortRange[1]-rule.GuestPortRange[0] > 0 {\n\t\t\t\terrs = errors.Join(errs, fmt.Errorf(\"field `%s.hostSocket` can only be mapped from a single port or socket. not a range\", field))\n\t\t\t}\n\t\t} else if rule.GuestPortRange[1]-rule.GuestPortRange[0] != rule.HostPortRange[1]-rule.HostPortRange[0] {\n\t\t\terrs = errors.Join(errs, fmt.Errorf(\"field `%s.hostPortRange` must specify the same number of ports as field `%s.guestPortRange`\", field, field))\n\t\t}\n\n\t\tif len(rule.HostSocket) >= osutil.UnixPathMax {\n\t\t\terrs = errors.Join(errs, fmt.Errorf(\"field `%s.hostSocket` must be less than UNIX_PATH_MAX=%d characters, but is %d\",\n\t\t\t\tfield, osutil.UnixPathMax, len(rule.HostSocket)))\n\t\t}\n\t\tswitch rule.Proto {\n\t\tcase limatype.ProtoTCP, limatype.ProtoUDP, limatype.ProtoAny:\n\t\tdefault:\n\t\t\terrs = errors.Join(errs, fmt.Errorf(\"field `%s.proto` must be %#q, %#q, or %#q\", field, limatype.ProtoTCP, limatype.ProtoUDP, limatype.ProtoAny))\n\t\t}\n\t\tif rule.Reverse && rule.GuestSocket == \"\" {\n\t\t\terrs = errors.Join(errs, fmt.Errorf(\"field `%s.reverse` must be %t\", field, false))\n\t\t}\n\t\tif rule.Reverse && rule.HostSocket == \"\" {\n\t\t\terrs = errors.Join(errs, fmt.Errorf(\"field `%s.reverse` must be %t\", field, false))\n\t\t}\n\t\t// Not validating that the various GuestPortRanges and HostPortRanges are not overlapping. Rules will be\n\t\t// processed sequentially and the first matching rule for a guest port determines forwarding behavior.\n\t}\n\tfor i, rule := range y.CopyToHost {\n\t\tfield := fmt.Sprintf(\"CopyToHost[%d]\", i)\n\t\tif rule.GuestFile != \"\" {\n\t\t\tif !path.IsAbs(rule.GuestFile) {\n\t\t\t\terrs = errors.Join(errs, fmt.Errorf(\"field `%s.guest` must be an absolute path, but is %#q\", field, rule.GuestFile))\n\t\t\t}\n\t\t}\n\t\tif rule.HostFile != \"\" {","sourceCodeStart":361,"sourceCodeEnd":397,"githubUrl":"https://github.com/lima-vm/lima/blob/dd909d0973cd84fa35f9e1693181b4585ea616c1/pkg/limayaml/validate.go#L361-L397","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Set the rule's proto to one of exactly: tcp, udp, or any (lowercase).","If the rule targets both protocols, use `any` rather than omitting or inventing a value.","Run `limactl validate <file>` before `start`/`edit` to catch the typo early."],"exampleFix":"# before\nportForwards:\n  - guestPort: 80\n    hostPort: 8080\n    proto: \"TCP\"\n# after\nportForwards:\n  - guestPort: 80\n    hostPort: 8080\n    proto: \"tcp\"","handlingStrategy":"validation","validationCode":"allowed := map[string]bool{\"tcp\": true, \"udp\": true, \"any\": true}\nfor i, r := range cfg.PortForwards {\n    if !allowed[r.Proto] {\n        return fmt.Errorf(\"PortForwards[%d].proto %q invalid; use tcp, udp, or any\", i, r.Proto)\n    }\n}","typeGuard":"func validProto(p string) bool { return p == \"tcp\" || p == \"udp\" || p == \"any\" }","tryCatchPattern":"err := limayaml.Validate(y, \"strict\")\nif err != nil {\n    var fieldErr *ValidationError\n    if errors.As(err, &fieldErr) { /* inspect joined field errors */ }\n    return fmt.Errorf(\"lima config invalid: %w\", err)\n}","preventionTips":["Always write proto values lowercase: tcp, udp, any.","Run `limactl validate <file>` in CI before limactl start.","Reuse limatype constants instead of string literals in generated configs."],"tags":["lima","config-validation","port-forwarding","yaml"],"backgroundTag":"schema-validation-failed","analyzedSha":"dd909d0973cd84fa35f9e1693181b4585ea616c1","analyzedAt":"2026-09-01T14:24:59.842Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}