JuliusBrussee/caveman · error
cachebench: observations exceed record limit %d
Error message
cachebench: observations exceed record limit %d
What it means
The JSONL observation reader caps how many records it will return: once limits.MaxRecords records have been parsed, the next non-empty line triggers this error instead of silently truncating. It fires mid-scan, so a partial slice is discarded (nil returned).
Source
Thrown at cacheengine/cachebench/observed.go:113
func ReadObservationJSONLWithLimits(reader io.Reader, limits ObservationReadLimits) ([]ObservationRecord, error) {
if limits.MaxLineBytes <= 0 || limits.MaxLineBytes > 64<<20 || limits.MaxRecords <= 0 || limits.MaxRecords > 1_000_000 {
return nil, errors.New("cachebench: invalid observation 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 []ObservationRecord
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: observations exceed record limit %d", limits.MaxRecords)
}
if !validUniqueJSONObject(raw) {
return nil, fmt.Errorf("cachebench: observation line %d: duplicate or invalid JSON", line)
}
decoder := json.NewDecoder(bytes.NewReader(raw))
decoder.DisallowUnknownFields()
var record ObservationRecord
if err := decoder.Decode(&record); err != nil {
return nil, fmt.Errorf("cachebench: observation line %d: %w", line, err)
}
var trailing any
if err := decoder.Decode(&trailing); err != io.EOF {
return nil, fmt.Errorf("cachebench: observation line %d: trailing JSON", line)
}
if record.Schema != ObservationSchema {
return nil, fmt.Errorf("cachebench: observation line %d: schema %q", line, record.Schema)
}
if strings.TrimSpace(record.RequestID) == "" || seen[record.RequestID] {View on GitHub (pinned to 27d5a3981a)
Solutions
- Raise limits.MaxRecords to at least the file's record count (count non-empty lines).
- Split the observation file and evaluate shards in separate runs.
- Subsample the replay so observations stay under the cap.
Example fix
// before limits.MaxRecords = 50_000 // after limits.MaxRecords = 500_000
Defensive patterns
Strategy: validation
Validate before calling
func countRecords(path string) (int, error) {
f, err := os.Open(path)
if err != nil {
return 0, err
}
defer f.Close()
n := 0
scanner := bufio.NewScanner(f)
for scanner.Scan() {
if len(bytes.TrimSpace(scanner.Bytes())) > 0 {
n++
}
}
return n, scanner.Err()
}
// if n > limits.MaxRecords { limits.MaxRecords = n } Prevention
- Pre-count observation lines and size MaxRecords accordingly.
- Split long observation files per epoch.
- Remember MaxLineBytes too: oversized lines fail the scanner before the record cap is reached.
When it happens
Trigger: Loading an observations file whose non-empty line count exceeds ReaderLimits.MaxRecords — the (MaxRecords+1)-th populated line errors.
Common situations: Long replays producing more observations than the configured budget; raising replay length without raising MaxRecords; concatenating observation files.
Related errors
- cachebench: invalid observation read limits
- cachebench: no observation records
- cachebench: provider population exceeds 1024
- cachebench: provider population exceeds 1024
- cacheengine: stable prefix exceeds configured byte limit
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/a469d185f857b964.
Report an issue: GitHub.