{"record":{"id":"1de571afdae43d42","repo":"netbirdio/netbird","slug":"unhandled-protocol-type-d","errorCode":null,"errorMessage":"unhandled protocol type: %d","messagePattern":"unhandled protocol type: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"client/cmd/expose.go","lineNumber":233,"sourceCode":"func 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\nfunc handleExposeReady(cmd *cobra.Command, stream proto.DaemonService_ExposeServiceClient, port uint64) error {\n\tevent, err := stream.Recv()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"receive expose event: %v\", status.Convert(err).Message())\n\t}\n\n\tready, ok := event.Event.(*proto.ExposeServiceEvent_Ready)\n\tif !ok {\n\t\treturn fmt.Errorf(\"unexpected expose event: %T\", event.Event)\n\t}\n\tprintExposeReady(cmd, ready.Ready, port)\n\treturn nil\n}\n\nfunc printExposeReady(cmd *cobra.Command, r *proto.ExposeServiceReady, port uint64) {","sourceCodeStart":215,"sourceCodeEnd":251,"githubUrl":"https://github.com/netbirdio/netbird/blob/93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c/client/cmd/expose.go#L215-L251","documentation":"The default arm of the switch in toExposeProtocol: ParseProtocolType returned a ProtocolType value that has no mapping to a proto.ExposeProtocol enum. Today ParseProtocolType can only return the values 0-4 and every one has a case, so this branch is dead code — a guard ensuring that any protocol constant added to client/internal/expose/protocol.go without a matching mapping here fails loudly instead of silently mapping to zero (EXPOSE_HTTP).","triggerScenarios":"A contributor adds a new constant to ProtocolType (e.g. ProtocolQUIC = 5) and extends ParseProtocolType to parse it, but forgets the corresponding case in toExposeProtocol's switch.","commonSituations":"Two-file feature changes where one hunk is lost in rebase; forks adding protocols to the expose package only; copy of the mapping drifting during refactors.","solutions":["As a user: not reachable in stock builds — if a custom build shows it, update/rebuild so both enums match","As a contributor: in the same change that adds the expose.ProtocolType constant, add its case mapping to the matching proto.ExposeProtocol_* value in toExposeProtocol"],"exampleFix":"// before: new constant parsed but not mapped -> hits default\ncase expose.ProtocolTLS:\n\treturn proto.ExposeProtocol_EXPOSE_TLS, nil\ndefault:\n\treturn 0, fmt.Errorf(\"unhandled protocol type: %d\", p)\n\n// after: add the mapping when introducing the constant\ncase expose.ProtocolTLS:\n\treturn proto.ExposeProtocol_EXPOSE_TLS, nil\ncase expose.ProtocolQUIC:\n\treturn proto.ExposeProtocol_EXPOSE_QUIC, nil\ndefault:\n\treturn 0, fmt.Errorf(\"unhandled protocol type: %d\", p)","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"func protocolTypeSupported(p expose.ProtocolType) bool {\n\tswitch p {\n\tcase expose.ProtocolHTTP, expose.ProtocolHTTPS, expose.ProtocolTCP, expose.ProtocolUDP, expose.ProtocolTLS:\n\t\treturn true\n\tdefault:\n\t\treturn false\n\t}\n}","tryCatchPattern":null,"preventionTips":["When adding a ProtocolType constant, add its proto mapping case in the same commit and run the expose tests","Treat this error text ('unhandled protocol type') as a build-time contract break, not a runtime condition to catch","Add a unit test enumerating every ParseProtocolType-accepted string through toExposeProtocol so drift fails CI"],"tags":["defensive","exhaustive-switch","protocol","dead-code-guard","netbird-cli"],"backgroundTag":null,"analyzedSha":"93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c","analyzedAt":"2026-08-16T03:09:19.136Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}