pion/webrtc · error

%w: stream count must be one

Error message

%w: stream count must be one

What it means

This error is returned by validateChannelMappingLayout when the channel mapping's streamCount is not exactly 1. The Ogg/Opus muxer in this library only supports single-stream (non-chained) Opus logical streams per track, so any channel mapping declaring more (or fewer) than one Opus stream is rejected before the identification header is written. It wraps errInvalidChannelMap so callers can errors.Is against it.

Source

Thrown at pkg/media/oggwriter/oggwriter.go:659

		mapping:      append([]byte(nil), mapping...),
	}, nil
}

func validateChannelMappingFamily(family uint8) error {
	switch family {
	case channelMappingFamily1, channelMappingFamily2, channelMappingFamily255:
		return nil
	default:
		return fmt.Errorf("%w: unsupported family %d", errInvalidChannelMap, family)
	}
}

func validateChannelMappingLayout(streamCount, coupledCount uint8, mapping []byte) error {
	if len(mapping) == 0 || len(mapping) > math.MaxUint8 {
		return errInvalidChannelCount
	}
	if streamCount != 1 {
		return fmt.Errorf("%w: stream count must be one", errInvalidChannelMap)
	}
	if coupledCount > streamCount {
		return fmt.Errorf("%w: coupled count exceeds stream count", errInvalidChannelMap)
	}

	decodedChannels := int(streamCount) + int(coupledCount)
	for _, channel := range mapping {
		if channel != math.MaxUint8 && int(channel) >= decodedChannels {
			return fmt.Errorf("%w: channel map entry is out of range", errInvalidChannelMap)
		}
	}

	return nil
}

func validateFamilySpecificChannelMapping(
	family uint8,
	streamCount uint8,

View on GitHub (pinned to 8c25dc09fa)

Solutions

  1. Set streamCount to 1 and encode all channels into that stream plus coupled streams (coupledCount <= streamCount).
  2. Encode each extra logical stream as a separate track instead of one multi-stream track.
  3. Keep the default mapping via defaultChannelMapping (mono: streamCount=1/coupled=0/family 0; stereo: streamCount=1/coupled=1/family 0) unless you specifically need a custom one.
  4. Check errors.Is(err, errInvalidChannelMap) to distinguish mapping errors from other writer errors.

Example fix

// before
validateChannelMapping(family, 2, 1, []byte{0, 1, 2, 3}) // streamCount=2
// after
validateChannelMapping(family, 1, 1, []byte{0, 1, 2, 3}) // streamCount must be 1
Defensive patterns

Strategy: validation

Validate before calling

func validStreamCount(streamCount uint8) bool { return streamCount == 1 }
if !validStreamCount(cfg.StreamCount) { return fmt.Errorf("streamCount must be 1, got %d", cfg.StreamCount) }

Type guard

func isSingleStreamMapping(m ChannelMapping) bool { return m.StreamCount == 1 }

Try / catch

if err := w.AddTrack(...); err != nil {
    if errors.Is(err, errInvalidChannelMap) {
        // fix channel mapping config before retrying
    }
    return err
}

Prevention

When it happens

Trigger: Passing a channel mapping with streamCount != 1 to validateChannelMapping (e.g. via a custom channel-mapping option when adding an Opus track to an oggwriter), such as streamCount=2 with coupledCount=0 intended to encode two mono streams.

Common situations: Developers copying channel mapping tables from multi-stream Opus sources (e.g. surround-sound 5.1 encodes that use several Opus streams) or porting RTP depacketization code where multiple streams were supported; also happens when the stream/coupled counts are swapped with the mapping length.

Related errors


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