JuliusBrussee/caveman · error

cachebench: no trace records

Error message

cachebench: no trace records

What it means

Returned by ReadTraceJSONLWithLimits when the scanner reached EOF without accumulating any records. Every line was either empty after bytes.TrimSpace or the input was empty entirely, so the parsed record slice has length 0. The library treats a record-less trace as an error because downstream replay/validation code assumes at least one trace record exists.

Source

Thrown at cacheengine/cachebench/trace.go:342

		if record.Schema == TraceSchema || record.Schema == TraceSchemaV2 {
			if record.ExpectedRPM <= 0 || record.ExpectedCalls <= 0 || !validTimingBasis(record.TimingBasis) {
				return nil, fmt.Errorf("cachebench: trace line %d: incomplete replay metadata", line)
			}
		}
		if record.Schema == TraceSchema {
			if record.DeclaredInputTokens <= 0 || record.DeclaredInputTokens < record.PrefixTokens || record.MaxOutputTokens <= 0 || !requestBudgetMatchesBody(record) {
				return nil, fmt.Errorf("cachebench: trace line %d: incomplete or mismatched billed-token budget", line)
			}
		}
		seen[record.RequestID] = true
		record.Body = append(json.RawMessage(nil), record.Body...)
		records = append(records, record)
	}
	if err := scanner.Err(); err != nil {
		return nil, fmt.Errorf("cachebench: read trace: %w", err)
	}
	if len(records) == 0 {
		return nil, errors.New("cachebench: no trace records")
	}
	return records, nil
}

// NativeRequest reconstructs exact optimizer input captured by v2 or v3 trace.
// Legacy v1 traces remain readable for observation joins, but cannot drive live
// replay because they omitted routing and economics inputs.
func (record TraceRecord) NativeRequest() (cacheengine.NativeRequest, error) {
	if record.Schema != TraceSchema && record.Schema != TraceSchemaV2 {
		return cacheengine.NativeRequest{}, fmt.Errorf("cachebench: request %q needs %s or %s for reconstruction", record.RequestID, TraceSchemaV2, TraceSchema)
	}
	if !validTraceIdentity(record) || !validTimingBasis(record.TimingBasis) || record.ExpectedRPM <= 0 || record.ExpectedCalls <= 0 || record.PrefixTokens < 0 || !validUniqueJSONObject(record.Body) || record.BodySHA256 != bodyDigest(record.Body) || record.StableSegmentCount < 0 || record.StableSegmentCount > len(record.Prefix) || !validPrefix(record.Prefix) {
		return cacheengine.NativeRequest{}, fmt.Errorf("cachebench: request %q has incomplete or invalid replay identity", record.RequestID)
	}
	return cacheengine.NativeRequest{
		Scope: record.Scope, Epoch: record.Epoch, PartitionKey: record.PartitionKey,
		ExpectedRequestsPerMinute: record.ExpectedRPM, ExpectedCalls: record.ExpectedCalls,
		Provider: record.Provider, Model: record.Model, Region: record.Region, Endpoint: record.Endpoint,

View on GitHub (pinned to 27d5a3981a)

Solutions

  1. Verify the trace file is non-empty and contains JSONL lines before parsing: check file size and first non-blank line
  2. If the file is unexpectedly empty, re-run the trace capture step that produced it
  3. Guard callers that legitimately may have no data by checking len(records)==0 upstream and skipping the parse entirely

Example fix

// before
records, err := cachebench.ReadTraceJSONL(f) // f is empty -> error

// after
info, _ := f.Stat()
if info.Size() == 0 {
    return fmt.Errorf("trace file %s is empty; regenerate it", path)
}
records, err := cachebench.ReadTraceJSONL(f)
Defensive patterns

Strategy: validation

Validate before calling

info, err := f.Stat()
if err != nil {
	return err
}
if info.Size() == 0 {
	return errors.New("trace file is empty; skipping parse")
}
records, err := cachebench.ReadTraceJSONL(f)

Try / catch

records, err := cachebench.ReadTraceJSONL(r)
if err != nil {
	if strings.Contains(err.Error(), "no trace records") {
		log.Print("empty trace; nothing to replay")
		return nil, nil
	}
	return nil, err
}

Prevention

When it happens

Trigger: Passing an empty io.Reader or a file containing only whitespace/newlines to ReadTraceJSONL or ReadTraceJSONLWithLimits. Pointing -trace at a zero-byte file or a file whose lines are all blank. Passing an os.File opened on a path that was truncated by a previous failed write.

Common situations: Trace capture job failed midway and left an empty file; CI pipeline passes an artifact path before the artifact is downloaded; shell redirect typo created an empty file (e.g. '> trace.jsonl' run without the producing command); passing bytes.NewReader(nil) in tests.

Related errors


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