{"record":{"id":"fa330f73958e83ff","repo":"gravitational/teleport","slug":"decoded-unknown-tdpb-message","errorCode":null,"errorMessage":"decoded unknown TDPB message","messagePattern":"decoded unknown TDPB message","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"lib/srv/desktop/tdp/protocol/tdpb/tdpb.go","lineNumber":40,"sourceCode":"\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"io\"\n\n\t\"github.com/gravitational/trace\"\n\t\"google.golang.org/protobuf/proto\"\n\n\ttdpbv1 \"github.com/gravitational/teleport/api/gen/proto/go/teleport/desktop/v1\"\n\t\"github.com/gravitational/teleport/lib/srv/desktop/tdp\"\n)\n\n// ProtocolName is the identifier for the TDPB protocol.\nconst ProtocolName = \"teleport-tdpb-1.0\"\n\n// ErrUnknownMessage is returned when an unknown message is decoded.\nvar ErrUnknownMessage = errors.New(\"decoded unknown TDPB message\")\n\n// ErrIsTDP is returned when a legacy TDP message is received\n// during or after a connection upgrade to TDPB.\nvar ErrIsTDP = errors.New(\"message is TDP, not TDPB\")\n\nconst (\n\t// We can differentiate between TDP and TDPB messages on the wire\n\t// by inspecting the first byte received. A non-empty first byte\n\t// is presumed to be a TDP message, otherwise, TDPB.\n\t// Since the first byte of a TDPB message is the high 8 bits of its\n\t// length, we must take care not to allow TDPB messages that\n\t// meet or exceed length 2^24 (16MiB).\n\t// Once TDP is fully deprecated we can relax this constraint, although\n\t// it's unlikely we would ever want messages anywhere near this size.\n\tmaxMessageLength = (1 << 24) - 1\n\ttdpbHeaderLength = 4 // sizeof(uint32)\n)\n","sourceCodeStart":22,"sourceCodeEnd":58,"githubUrl":"https://github.com/gravitational/teleport/blob/1283425b60ec5f60d509ba4c791183d452923ff7/lib/srv/desktop/tdp/protocol/tdpb/tdpb.go#L22-L58","documentation":"Sentinel error from the TDPB protocol package returned by DecodeStrict/DecodePermissive when the decoded message type is not recognized (an unknown message ID). It indicates a message from a newer/unsupported protocol implementation.","triggerScenarios":"Receiving a TDPB frame whose message type byte does not map to any known message struct — typically a message defined in a newer Teleport version than the decoder.","commonSituations":"Version skew between Teleport components (newer proxy/desktop service sends messages an older binary can't decode); wire corruption; test code iterating messages uses errors.Is to skip unknown ones.","solutions":["Upgrade the decoding binary so both ends share the same TDPB message vocabulary","Use errors.Is(err, tdpb.ErrUnknownMessage) to skip and continue when tolerance is desired (as ReadAll-style helpers do)","If unexpected, log the raw message type byte and compare against the TDPB protocol registry"],"exampleFix":"// before\nmsg, err := tdpb.DecodeStrict(rdr)\nif err != nil { return err }\n// after\nmsg, err := tdpb.DecodeStrict(rdr)\nif errors.Is(err, tdpb.ErrUnknownMessage) { continue }\nif err != nil { return trace.Wrap(err) }","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"msg, err := tdpb.DecodeStrict(rdr)\nif errors.Is(err, tdpb.ErrUnknownMessage) {\n    continue // skip unsupported message from newer peer\n}\nif err != nil { return nil, trace.Wrap(err) }","preventionTips":["Keep both protocol endpoints on compatible Teleport versions","Use errors.Is on the sentinel to skip unknown messages","Log unknown message type bytes for upgrade planning"],"tags":["tdpb","protocol","version-skew"],"backgroundTag":"unknown-message-type","analyzedSha":"1283425b60ec5f60d509ba4c791183d452923ff7","analyzedAt":"2026-09-02T04:06:41.601Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}