dagger/dagger · error

decode length: truncated varint

Error message

decode length: truncated varint

What it means

rowDecoder.length decodes an unsigned varint used as a length prefix for string/bytes fields. binary.Uvarint returning n==0 means the buffer ran out before the length varint terminated. The string/bytes readers (which call length) cannot determine how many bytes to take, so the row is treated as corrupt.

Source

Thrown at engine/clientdb/store_codec.go:96

	if d.off == len(d.buf) {
		return false, fmt.Errorf("decode bool: unexpected end of row")
	}
	v := d.buf[d.off]
	d.off++
	switch v {
	case 0:
		return false, nil
	case 1:
		return true, nil
	default:
		return false, fmt.Errorf("decode bool: invalid value %d", v)
	}
}

func (d *rowDecoder) length() (int, error) {
	v, n := binary.Uvarint(d.buf[d.off:])
	if n == 0 {
		return 0, fmt.Errorf("decode length: truncated varint")
	}
	if n < 0 {
		return 0, fmt.Errorf("decode length: varint overflow")
	}
	d.off += n
	if v > uint64(maxInt) {
		return 0, fmt.Errorf("decode length: %d overflows int", v)
	}
	return int(v), nil
}

func (d *rowDecoder) take(n int) ([]byte, error) {
	if n < 0 || n > len(d.buf)-d.off {
		return nil, fmt.Errorf("decode field of length %d: only %d bytes remain", n, len(d.buf)-d.off)
	}
	v := d.buf[d.off : d.off+n]
	d.off += n
	return v, nil

View on GitHub (pinned to 82ba2681db)

Solutions

  1. Check row boundary computation in the caller: ensure each decodeSpan/decodeLog starts exactly at a row start, not mid-row.
  2. Skip the corrupt/truncated row and re-ingest the span/log from the source.
  3. Store row length explicitly and validate buf length before decoding.
  4. Add encode/decode round-trip tests for string and bytes fields to catch writer truncation bugs.

Example fix

// before
s, err := dec.string() // panics-free but fails: truncated length varint
// after
s, err := dec.string()
if err != nil {
    return nil, fmt.Errorf("skipping corrupt row: %w", err)
}
Defensive patterns

Strategy: validation

Validate before calling

if _, n := binary.Uvarint(buf[off:]); n == 0 {
    return fmt.Errorf("incomplete length prefix at offset %d", off)
}

Try / catch

s, err := dec.string()
if err != nil {
    return nil, fmt.Errorf("skip corrupt row: %w", err)
}

Prevention

When it happens

Trigger: rowDecoder.string or rowDecoder.bytes called (from decodeSpan/decodeLog) on a row whose buffer ends inside the length prefix — truncated blob, short read from the store, or an offset pointing at the final bytes of a row.

Common situations: Partial row writes after a crash; reading a row that was clipped by a size limit; iterating rows with wrong boundaries so a decoder starts mid-row and runs out of bytes.

Related errors


AI-assisted analysis of dagger/dagger@82ba2681db (2026-09-05). Data as JSON: /api/errors/f8ce4b05b8a09258. Report an issue: GitHub.