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, nilView on GitHub (pinned to 82ba2681db)
Solutions
- Check row boundary computation in the caller: ensure each decodeSpan/decodeLog starts exactly at a row start, not mid-row.
- Skip the corrupt/truncated row and re-ingest the span/log from the source.
- Store row length explicitly and validate buf length before decoding.
- 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
- Validate each row's full byte length before starting decode.
- Iterate rows strictly by recorded boundaries, never by scanning bytes.
- Fuzz encode/decode round trips to catch truncation bugs.
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
- decode int64: truncated varint
- decode int64: varint overflow
- decode length: varint overflow
- decode bool: unexpected end of row
- decode bool: invalid value %d
AI-assisted analysis of dagger/dagger@82ba2681db (2026-09-05).
Data as JSON: /api/errors/f8ce4b05b8a09258.
Report an issue: GitHub.