{"record":{"id":"dcf61dd4b4f6e6f8","repo":"netbirdio/netbird","slug":"invalid-protocol-w","errorCode":null,"errorMessage":"invalid protocol: %w","messagePattern":"invalid protocol: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"client/cmd/expose.go","lineNumber":218,"sourceCode":"\t\treq.ListenPort = uint32(resolveExternalPort(port))\n\t}\n\n\tstream, err := client.ExposeService(ctx, req)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"expose service: %v\", status.Convert(err).Message())\n\t}\n\n\tif err := handleExposeReady(cmd, stream, port); err != nil {\n\t\treturn err\n\t}\n\n\treturn waitForExposeEvents(cmd, ctx, stream)\n}\n\nfunc toExposeProtocol(exposeProtocol string) (proto.ExposeProtocol, error) {\n\tp, err := expose.ParseProtocolType(exposeProtocol)\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"invalid protocol: %w\", err)\n\t}\n\n\tswitch p {\n\tcase expose.ProtocolHTTP:\n\t\treturn proto.ExposeProtocol_EXPOSE_HTTP, nil\n\tcase expose.ProtocolHTTPS:\n\t\treturn proto.ExposeProtocol_EXPOSE_HTTPS, nil\n\tcase expose.ProtocolTCP:\n\t\treturn proto.ExposeProtocol_EXPOSE_TCP, nil\n\tcase expose.ProtocolUDP:\n\t\treturn proto.ExposeProtocol_EXPOSE_UDP, nil\n\tcase expose.ProtocolTLS:\n\t\treturn proto.ExposeProtocol_EXPOSE_TLS, nil\n\tdefault:\n\t\treturn 0, fmt.Errorf(\"unhandled protocol type: %d\", p)\n\t}\n}\n","sourceCodeStart":200,"sourceCodeEnd":236,"githubUrl":"https://github.com/netbirdio/netbird/blob/93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c/client/cmd/expose.go#L200-L236","documentation":"toExposeProtocol wraps the error from expose.ParseProtocolType: the protocol string is not http, https, tcp, udp, or tls. In the normal CLI flow this is effectively unreachable, because exposeFn calls validateExposeFlags first and isProtocolValid rejects the exact same set before any RPC; it fires only when toExposeProtocol runs without that pre-validation (programmatic use, a refactor dropping the validate call, or future drift between the two whitelists).","triggerScenarios":"Calling toExposeProtocol directly from code without running validateExposeFlags; a code path that bypasses exposeFn; the accepted set in isProtocolValid diverging from ParseProtocolType after an edit.","commonSituations":"Reusing the CLI helper from another tool or test; refactors that reorder validation; a contributor extending one switch but not the other.","solutions":["As a CLI user: pass a valid protocol (http, https, tcp, udp, tls) — if you see this, also check NB_PROTOCOL","As a developer: run validateExposeFlags (or an equivalent whitelist check) before calling toExposeProtocol, keeping both lists in sync"],"exampleFix":"// before\nproto, err := toExposeProtocol(exposeProtocol) // may fail\n\n// after\nif _, err := validateExposeFlags(cmd, portStr); err != nil {\n\treturn err\n}\nproto, err := toExposeProtocol(exposeProtocol)","handlingStrategy":"validation","validationCode":"if _, err := expose.ParseProtocolType(protoFlag); err != nil {\n\tlog.Fatalf(\"%v; use http, https, tcp, udp, or tls\", err)\n}","typeGuard":"func isParseableProtocol(p string) bool {\n\t_, err := expose.ParseProtocolType(p)\n\treturn err == nil\n}","tryCatchPattern":null,"preventionTips":["Keep a single source of truth: validate with the same parser (expose.ParseProtocolType) you will convert with","In the CLI flow, always run validateExposeFlags before toExposeProtocol","When editing either the valid-set switch or the parser, update both in the same change"],"tags":["cli","validation","protocol","defensive","netbird-cli"],"backgroundTag":null,"analyzedSha":"93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c","analyzedAt":"2026-08-16T03:09:19.136Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}