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

  1. Add all tracks before writing the first RTP packet
  2. Create a new oggwriter instance if you need to add tracks after writing started
  3. 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

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


AI-assisted analysis of pion/webrtc@8c25dc09fa (2026-09-03). Data as JSON: /api/errors/4d4bb25affe114d7. Report an issue: GitHub.