pion/webrtc · error
cannot add Ogg tracks after writing has started
Error message
cannot add Ogg tracks after writing has started
What it means
errTracksStarted is returned by NewTrack when an attempt is made to add a track after the writer has begun writing Ogg pages (w.started is true). Ogg stream headers must be written before any data pages, so late tracks cannot be interleaved into an in-progress file. It is a lifecycle/state error of the writer.
Source
Thrown at pkg/media/oggwriter/oggwriter.go:53
channelMappingFamily2 = 2
channelMappingFamily255 = 255
idPageSignature = "OpusHead"
commentPageSignature = "OpusTags"
defaultVendor = "pion"
pageHeaderSignature = "OggS"
pageHeaderSize = 27
maxOggPageSegments = 255
noGranulePosition = ^uint64(0)
maxUint32Length = uint64(1<<32 - 1)
)
var (
errFileNotOpened = errors.New("file not opened")
errOutputNotOpened = errors.New("output not opened")
errInvalidNilPacket = errors.New("invalid nil packet")
errDuplicateTrackSSRC = errors.New("duplicate Ogg track SSRC")
errDuplicateTrackSerial = errors.New("duplicate Ogg track serial")
errTracksStarted = errors.New("cannot add Ogg tracks after writing has started")
errPacketSSRCMismatch = errors.New("RTP packet SSRC does not match Ogg track SSRC")
errInvalidOpusPacket = errors.New("invalid Opus packet")
errInvalidChannelCount = errors.New("invalid channel count")
errInvalidChannelMap = errors.New("invalid channel mapping")
errInvalidOpusTags = errors.New("invalid OpusTags")
)
type pageRewriter interface {
io.Seeker
io.WriterAt
}
type writerConfig struct {
sampleRate uint32
channelMapping channelMapping
pageRewriter pageRewriter
opusTags OpusTags
}View on GitHub (pinned to 8c25dc09fa)
Solutions
- Add all tracks before writing the first RTP packet
- Create a new oggwriter instance if you need to add tracks after writing started
- Restructure so track discovery/completion happens before recording begins
Example fix
// before track1, _ := writer.NewTrack(1111, nil) _ = track1.WriteRTP(pkt) // writer now started track2, err := writer.NewTrack(2222, nil) // errTracksStarted // after track1, _ := writer.NewTrack(1111, nil) track2, _ := writer.NewTrack(2222, nil) _ = track1.WriteRTP(pkt)
Defensive patterns
Strategy: validation
Validate before calling
if writer.Started() { // or track via your own flag set after first WriteRTP
return errors.New("cannot add track after writing started")
} Try / catch
track, err := writer.NewTrack(ssrc, opts)
if errors.Is(err, oggwriter.errTracksStarted) {
writer, err = newWriterAndReaddTracks() // recreate writer for late tracks
} Prevention
- Create all tracks immediately after opening the writer
- Never interleave NewTrack calls with WriteRTP calls
- Structure the pipeline so track setup completes before the first packet
- If dynamic tracks are required, open a new writer per phase
When it happens
Trigger: Calling writer.NewTrack(...) after any packet write has started the writer — e.g. after track.WriteRTP has flushed headers and data pages, then calling NewTrack again.
Common situations: Dynamically adding audio tracks mid-recording; a code path that adds a fallback/second track after writing has already begun; tests asserting the writer rejects late tracks.
Related errors
- stream is nil
- bad header signature
- bad opus tags signature
- duplicate Ogg track serial
- RTP packet SSRC does not match Ogg track SSRC
AI-assisted analysis of pion/webrtc@8c25dc09fa (2026-09-03).
Data as JSON: /api/errors/4d4bb25affe114d7.
Report an issue: GitHub.