JuliusBrussee/caveman · error

cachebench: invalid trace read limits

Error message

cachebench: invalid trace read limits

What it means

Returned by ReadTraceJSONLWithLimits in cacheengine/cachebench/trace.go when the caller-supplied TraceReadLimits fail validation: MaxLineBytes must be >0 and <=512MiB, MaxRecords must be >0 and <=1,000,000, and MaxBodyBytes must be >0 and <=256MiB. The library enforces these ceilings because trace parsing is resource-intensive and unbounded limits would allow memory exhaustion from a malformed or hostile trace file. It is a programmer-error guard, not a data error: the limits struct itself is invalid before any I/O happens.

Source

Thrown at cacheengine/cachebench/trace.go:281

	MaxLineBytes int
	MaxRecords   int
	MaxBodyBytes int
}

// DefaultTraceReadLimits supports public-corpus traces while bounding retained memory.
func DefaultTraceReadLimits() TraceReadLimits {
	return TraceReadLimits{MaxLineBytes: 96 << 20, MaxRecords: 100_000, MaxBodyBytes: 64 << 20}
}

// ReadTraceJSONL reads trace records using conservative default resource limits.
func ReadTraceJSONL(reader io.Reader) ([]TraceRecord, error) {
	return ReadTraceJSONLWithLimits(reader, DefaultTraceReadLimits())
}

// ReadTraceJSONLWithLimits reads strict trace JSONL under explicit resource limits.
func ReadTraceJSONLWithLimits(reader io.Reader, limits TraceReadLimits) ([]TraceRecord, error) {
	if limits.MaxLineBytes <= 0 || limits.MaxLineBytes > 512<<20 || limits.MaxRecords <= 0 || limits.MaxRecords > 1_000_000 || limits.MaxBodyBytes <= 0 || limits.MaxBodyBytes > 256<<20 {
		return nil, errors.New("cachebench: invalid trace read limits")
	}
	scanner := bufio.NewScanner(reader)
	initial := 64 * 1024
	if limits.MaxLineBytes < initial {
		initial = limits.MaxLineBytes
	}
	scanner.Buffer(make([]byte, initial), limits.MaxLineBytes)
	seen := map[string]bool{}
	var records []TraceRecord
	for line := 1; scanner.Scan(); line++ {
		raw := bytes.TrimSpace(scanner.Bytes())
		if len(raw) == 0 {
			continue
		}
		if len(records) >= limits.MaxRecords {
			return nil, fmt.Errorf("cachebench: trace exceeds record limit %d", limits.MaxRecords)
		}
		if !validUniqueJSONObject(raw) {

View on GitHub (pinned to 27d5a3981a)

Solutions

  1. Call ReadTraceJSONL(reader) instead, which passes DefaultTraceReadLimits() (MaxLineBytes 96MiB, MaxRecords 100k, MaxBodyBytes 64MiB) and is valid by construction
  2. If custom limits are needed, clamp each field into range before the call: 0 < MaxLineBytes <= 512<<20, 0 < MaxRecords <= 1_000_000, 0 < MaxBodyBytes <= 256<<20
  3. Log the limits struct values before calling so the offending field is obvious
  4. If genuinely larger traces must be parsed, split the trace file and process in batches so MaxRecords stays under 1,000,000

Example fix

// before
limits := cachebench.TraceReadLimits{MaxLineBytes: 1 << 30} // only one field set; others are 0
records, err := cachebench.ReadTraceJSONLWithLimits(r, limits)

// after
records, err := cachebench.ReadTraceJSONLWithLimits(r, cachebench.DefaultTraceReadLimits())
// or clamp explicitly:
// limits.MaxRecords = min(max(limits.MaxRecords, 1), 1_000_000) etc.
Defensive patterns

Strategy: validation

Validate before calling

func validTraceLimits(l cachebench.TraceReadLimits) bool {
	return l.MaxLineBytes > 0 && l.MaxLineBytes <= 512<<20 &&
		l.MaxRecords > 0 && l.MaxRecords <= 1_000_000 &&
		l.MaxBodyBytes > 0 && l.MaxBodyBytes <= 256<<20
}

if !validTraceLimits(limits) {
	return fmt.Errorf("limits out of range: %+v", limits)
}
records, err := cachebench.ReadTraceJSONLWithLimits(r, limits)

Try / catch

records, err := cachebench.ReadTraceJSONLWithLimits(r, limits)
if err != nil {
	if err.Error() == "cachebench: invalid trace read limits" {
		return fmt.Errorf("config bug: trace limits invalid: %+v", limits)
	}
	return fmt.Errorf("read trace: %w", err)
}

Prevention

When it happens

Trigger: Calling cachebench.ReadTraceJSONLWithLimits(reader, limits) with any field zero/negative, or with MaxLineBytes > 512<<20, MaxRecords > 1_000_000, or MaxBodyBytes > 256<<20. Copying limits from user input or config without clamping, or building a partial struct (Go zero values make unset ints 0, which fails the <=0 check).

Common situations: Setting limits from a YAML/JSON config where a missing key deserializes as 0; passing math.MaxInt to 'disable' a cap; unit tests constructing TraceReadLimits with only one field set; upgrading from an older version that accepted arbitrary limits.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15). Data as JSON: /api/errors/cfd55a51ce20a0d3. Report an issue: GitHub.