pion/webrtc · error

: payload too short for vendor+comment count

Error message

%w: payload too short for vendor+comment count

What it means

parseVendorString also verifies that room remains after the vendor string for the 4-byte user-comment count. If vendorStart+vendorLen plus the 4-byte count field extends beyond the payload, the packet is truncated after the vendor string and the wrapped errBadOpusTagsSignature sentinel is returned.

Solutions

  1. Ensure the full OpusTags packet, including the comment count (and comment entries), is present before parsing; concatenate all pages of the header packet.
  2. Re-generate or re-download the file; the source stream is truncated, so fix the writer or re-mux the media.
  3. At the call site, check errors.Is(err, ErrBadOpusTagsSignature) and treat the metadata as invalid rather than proceeding.

Example fix

// before
tags, err := oggreader.ParseOpusTags(payload[:8+4+vendorLen]) // count field cut off
// after
if len(payload) >= 8+4+vendorLen+4 {
    tags, err = oggreader.ParseOpusTags(payload)
}
Defensive patterns

Strategy: validation

Validate before calling

if len(payload) >= 12 {
    vendorLen := int(binary.LittleEndian.Uint32(payload[8:12]))
    if 12+vendorLen+4 <= len(payload) {
        tags, err := oggreader.ParseOpusTags(payload)
    }
}

Type guard

func hasVendorAndCommentCount(p []byte) bool {
    if len(p) < 12 {
        return false
    }
    vendorLen := int(binary.LittleEndian.Uint32(p[8:12]))
    return 12+vendorLen+4 <= len(p)
}

Try / catch

tags, err := oggreader.ParseOpusTags(payload)
if errors.Is(err, oggreader.ErrBadOpusTagsSignature) {
    // header truncated before comment count: skip metadata or re-fetch stream
}

Prevention

When it happens

Trigger: ParseOpusTags on a payload where the vendor string occupies bytes up to (or past) the end, leaving no 4-byte comment count — e.g. a header with a correct magic and vendor length but cut off before the following uint32.

Common situations: Tests simulating a missing comment count (TestParseVendorStringMissingCommentCount); truncated comment headers from partial writes or interrupted downloads; hand-crafted/fuzzed OpusTags packets missing the trailing fields.

Related errors


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

Appendix: source

Thrown at pkg/media/oggreader/oggreader.go:434

	got := HeaderType(payload[:8])
	if got != HeaderOpusTags {
		return fmt.Errorf("%w: expected %q, got %q", errBadOpusTagsSignature, HeaderOpusTags, got)
	}

	return nil
}

func parseVendorString(payload []byte, headerMagicLen, u32Size, minHeaderLen int) (string, int, error) {
	vendorLen32 := binary.LittleEndian.Uint32(payload[headerMagicLen : headerMagicLen+u32Size])
	if int(vendorLen32) > len(payload)-minHeaderLen {
		return "", 0, fmt.Errorf("%w: payload too short for vendor string", errBadOpusTagsSignature)
	}
	vendorLen := int(vendorLen32)

	vendorStart := headerMagicLen + u32Size
	vendorEnd := vendorStart + vendorLen
	if vendorEnd+u32Size > len(payload) {
		return "", 0, fmt.Errorf("%w: payload too short for vendor+comment count", errBadOpusTagsSignature)
	}

	vendor := string(payload[vendorStart:vendorEnd])

	return vendor, vendorEnd, nil
}

func parseUserComments(payload []byte, vendorEnd, u32Size int) ([]UserComment, error) {
	userCommentCount32 := binary.LittleEndian.Uint32(payload[vendorEnd : vendorEnd+u32Size])
	if int(userCommentCount32) > (len(payload)-vendorEnd)/u32Size {
		return nil, fmt.Errorf("%w: unreasonable comment count", errBadOpusTagsSignature)
	}
	userCommentCount := int(userCommentCount32)

	pos := vendorEnd + u32Size
	userComments := make([]UserComment, userCommentCount)

	for i := range userComments {

View on GitHub (pinned to 8c25dc09fa)