AlexxIT/go2rtc · warning

tlv8: zero item

Error message

tlv8: zero item

What it means

A TLV8 record with length byte L == 0 carries no value; the library treats this as a non-critical condition: it skips the 2-byte header (returning b[2:], so decoding continues) but still reports "tlv8: zero item". Callers like unmarshalStruct ignore the error and keep looping; unmarshalSlice uses the non-nil remaining bytes to advance to the next array element. It typically signals a separator/empty record or a struct field encoded with length 0.

Solutions

  1. No action needed if fields are intentionally empty: the library skips L=0 records and continues; treat this as informational.
  2. If the missing value matters, validate after decoding that the expected struct fields are non-zero before using the result.
  3. If you control the encoder, encode empty values with the correct tag/length rather than bare tag,0x00 records.
  4. Confirm the payload source matches the expected TLV8 schema (tags and field order) — a zero item where data is expected usually means wrong payload type.

Example fix

// before
tlv8.Unmarshal(data, &resp) // assume all fields populated
usePairing(resp.Identifier) // Identifier empty -> zero item was skipped

// after
tlv8.Unmarshal(data, &resp)
if len(resp.Identifier) == 0 {
    return errors.New("peer sent empty identifier (tlv8 zero item)")
}
Defensive patterns

Strategy: validation

Validate before calling

// after decoding, verify required fields were populated
if err := tlv8.Unmarshal(data, &resp); err != nil {
    return err
}
if resp.Identifier == nil || len(resp.Identifier) == 0 {
    return errors.New("required tlv8 field 1 (identifier) missing or empty")
}

Try / catch

// zero item is non-critical: the library skips it, so validate results afterwards
if err := tlv8.Unmarshal(data, &resp); err != nil {
    return err
}
if reflect.ValueOf(resp).IsZero() {
    return errors.New("tlv8 payload decoded to empty result (zero items only)")
}

Prevention

When it happens

Trigger: Any Unmarshal call whose payload contains a record with L=0 — e.g. a field encoded as tag,0x00 with no value (often an empty struct tag written by appendValue as tag,0x00), or a 0xFF,0x00 array separator — decoded through unmarshalTLV from unmarshalSlice/unmarshalStruct.

Common situations: Empty optional fields in HAP pairing payloads (e.g. zero-length identifiers); payloads produced by other TLV8 implementations that encode empty values as L=0; separators between array items misread as zero-length records; decoding data from a non-Go peer with different empty-value conventions.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


AI-assisted analysis of AlexxIT/go2rtc@c245815e75 (2026-09-07). Data as JSON: /api/errors/740495cc4fc668db. Report an issue: GitHub.

Appendix: source

Thrown at pkg/hap/tlv8/tlv8.go:237

	}

	return errors.New("tlv8: not implemented: " + kind.String())
}

// unmarshalTLV can return two types of errors:
// - critical and then the value of []byte will be nil
// - not critical and then []byte will contain the value
func unmarshalTLV(b []byte, value reflect.Value) ([]byte, error) {
	if len(b) < 2 {
		return nil, errors.New("tlv8: wrong size: " + value.Type().Name())
	}

	t := b[0]
	l := int(b[1])

	// array item divider (t == 0x00 || t == 0xFF)
	if l == 0 {
		return b[2:], errors.New("tlv8: zero item")
	}

	var v []byte

	for {
		if len(b) < 2+l {
			return nil, errors.New("tlv8: wrong size: " + value.Type().Name())
		}

		v = append(v, b[2:2+l]...)
		b = b[2+l:]

		// if size == 255 and same tag - continue read big payload
		if l < 255 || len(b) < 2 || b[0] != t {
			break
		}

		l = int(b[1])

View on GitHub (pinned to c245815e75)