AlexxIT/go2rtc · error

tlv8: unmarshal zero data

Error message

tlv8: unmarshal zero data

What it means

tlv8.Unmarshal rejects empty input: TLV8 data must contain at least one tag/length/value record, so len(data)==0 is treated as a decoding error rather than leaving the target zero-valued. It protects callers from silently treating an empty body as a valid message.

Solutions

  1. Check len(data) (or the decoded base64 string) is non-zero before calling Unmarshal and treat empty as a protocol/transport-level problem.
  2. Inspect why the producer sent zero bytes: truncated read, empty request body, or upstream error — fix at the source.
  3. If an empty payload is legitimately meaningful in your protocol, guard it in caller code instead of routing it through Unmarshal.
  4. For UnmarshalReader, verify Content-Length > 0 or that io.ReadAll returned data before decoding.

Example fix

// before
var msg message
if err := tlv8.UnmarshalBase64(req.EncryptedData, &msg); err != nil { return err } // fails on ""

// after
if req.EncryptedData == "" {
    return errors.New("empty tlv8 payload from peer")
}
var msg message
if err := tlv8.UnmarshalBase64(req.EncryptedData, &msg); err != nil { return err }
Defensive patterns

Strategy: validation

Validate before calling

func safeUnmarshal(data []byte, v any) error {
    if len(data) == 0 {
        return errors.New("peer sent empty tlv8 payload")
    }
    return tlv8.Unmarshal(data, v)
}

Try / catch

if err := tlv8.UnmarshalReader(req.Body, req.ContentLength, &msg); err != nil {
    if strings.Contains(err.Error(), "zero data") {
        return errors.New("empty TLV8 body from peer; check request format")
    }
    return err
}

Prevention

When it happens

Trigger: Calling tlv8.Unmarshal(nil, &v), tlv8.Unmarshal([]byte{}, &v), or via UnmarshalBase64 with an empty/whitespace-only string, or UnmarshalReader when the reader yields zero bytes (e.g. HTTP request with Content-Length 0 or an empty body).

Common situations: Peer sent an empty HTTP body with a TLV8 content type; reading a truncated response; decoding an empty base64 field from persisted state; off-by-one slicing that produced an empty slice; a client sending a keep-alive/empty POST that the server tries to decode.

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/2f317906d050800c. Report an issue: GitHub.

Appendix: source

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

	var data []byte
	var err error

	if n > 0 {
		data = make([]byte, n)
		_, err = io.ReadFull(r, data)
	} else {
		data, err = io.ReadAll(r)
	}
	if err != nil {
		return err
	}

	return Unmarshal(data, v)
}

func Unmarshal(data []byte, v any) error {
	if len(data) == 0 {
		return errors.New("tlv8: unmarshal zero data")
	}

	value := reflect.ValueOf(v)
	kind := value.Kind()

	if kind != reflect.Pointer {
		return errors.New("tlv8: value should be pointer: " + kind.String())
	}

	value = value.Elem()
	kind = value.Kind()

	if kind == reflect.Interface {
		value = value.Elem()
		kind = value.Kind()
	}

	switch kind {

View on GitHub (pinned to c245815e75)