{"record":{"id":"de0d3fe9341d2903","repo":"livekit/livekit","slug":"cannot-start-on-rtx-packet","errorCode":null,"errorMessage":"cannot start on rtx packet","messagePattern":"cannot start on rtx packet","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"pkg/sfu/buffer/buffer_base.go","lineNumber":779,"sourceCode":"\t\t\tif flowState.UnhandledReason == rtpstats.RTPFlowUnhandledReasonNone && !flowState.IsOutOfOrder {\n\t\t\t\tif err := b.snRangeMap.ExcludeRange(flowState.ExtSequenceNumber, flowState.ExtSequenceNumber+1); err != nil {\n\t\t\t\t\tb.logger.Errorw(\n\t\t\t\t\t\t\"could not exclude range\", err,\n\t\t\t\t\t\t\"sequenceNumber\", sn,\n\t\t\t\t\t\t\"extSequenceNumber\", flowState.ExtSequenceNumber,\n\t\t\t\t\t\t\"rtpStats\", b.rtpStats,\n\t\t\t\t\t\t\"rtpStatsLite\", b.rtpStatsLite,\n\t\t\t\t\t\t\"snRangeMap\", b.snRangeMap,\n\t\t\t\t\t\t\"skipped\", skippedSeqs,\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// do not start on an RTX packet\n\tif isRTX && !b.rtpStats.IsActive() {\n\t\treturn 0, errors.New(\"cannot start on rtx packet\")\n\t}\n\n\tflowState := b.rtpStats.Update(\n\t\tarrivalTime,\n\t\trtpPacket.Header.SequenceNumber,\n\t\trtpPacket.Header.Timestamp,\n\t\trtpPacket.Header.Marker,\n\t\trtpPacket.Header.MarshalSize(),\n\t\tlen(rtpPacket.Payload),\n\t\tint(rtpPacket.Header.PaddingSize),\n\t)\n\tswitch flowState.UnhandledReason {\n\tcase rtpstats.RTPFlowUnhandledReasonNone:\n\tcase rtpstats.RTPFlowUnhandledReasonRestart:\n\t\tif !b.enableStreamRestartDetection {\n\t\t\treturn 0, fmt.Errorf(\"unhandled reason: %s\", flowState.UnhandledReason.String())\n\t\t}\n","sourceCodeStart":761,"sourceCodeEnd":797,"githubUrl":"https://github.com/livekit/livekit/blob/ee45c3f0b1a83bf4352dbccb6607ebf70b2a5de6/pkg/sfu/buffer/buffer_base.go#L761-L797","documentation":"The stream buffer refuses to begin RTP stats tracking when the first packet processed is a retransmission (RTX) while rtpStats is not yet active. Starting a stream on an RTX packet would corrupt sequence-number accounting because the original sequence number belongs to an earlier stream position.","triggerScenarios":"A subscriber/track begins being forwarded and the first packet arriving at the buffer is an RTX retransmission (matched via the RTX SN cache or SN offset heuristic) before any original packet initializes stats.","commonSituations":"Joining a room mid-stream with packet loss where the very first received packet is a retransmission; publisher-side network producing RTX before original packets reach the SFU; track restart/reconnect scenarios.","solutions":["Usually transient: the next non-RTX packet initializes the stream; retry/let the track re-buffer.","Check publisher uplink quality — persistent early RTX indicates pre-join loss.","Upgrade LiveKit; RTX handling/repair logic has been improved across releases.","If reproducible, capture publisher-side traces to confirm RTX-before-original ordering."],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"// SFU-internal: treat as transient stream-start failure\nif err := buffer.Read(...); errors.Is(err, errCannotStartOnRTX) {\n  // wait for next non-RTX packet; do not tear down the track\n  continue\n}","preventionTips":["Maintain healthy publisher uplink to reduce pre-join retransmissions.","Keep LiveKit updated for RTX-start handling improvements.","Monitor error frequency per track; persistent occurrences signal upstream loss.","Avoid restarting tracks mid-loss-burst when possible."],"tags":["rtp","webrtc","sfu","rtx","media"],"backgroundTag":"rtx-retransmission-on-stream-start","analyzedSha":"ee45c3f0b1a83bf4352dbccb6607ebf70b2a5de6","analyzedAt":"2026-09-02T03:56:08.477Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-09T06:17:21.866Z"}