JuliusBrussee/caveman · error

cachebench: observation request %q provider/epoch mismatch

Error message

cachebench: observation request %q provider/epoch mismatch

What it means

An observation record joined by RequestID carries a Provider or Epoch that differs from the trace record's values. Provider and epoch are part of the observation's identity, so a mismatch means the observation was taken in a different context than the traced request.

Source

Thrown at cacheengine/cachebench/observed.go:62

		}
		if _, exists := byID[request.RequestID]; exists {
			return Report{}, fmt.Errorf("cachebench: duplicate trace request %q", request.RequestID)
		}
		if strings.TrimSpace(request.Provider) == "" || strings.TrimSpace(request.Epoch) == "" || !validUniqueJSONObject(request.Body) || request.BodySHA256 == "" || request.BodySHA256 != bodyDigest(request.Body) {
			return Report{}, fmt.Errorf("cachebench: trace request %q has invalid identity, body, or digest", request.RequestID)
		}
		if request.StableSegmentCount < 0 || request.StableSegmentCount > len(request.Prefix) || !validPrefix(request.Prefix) {
			return Report{}, fmt.Errorf("cachebench: trace request %q has invalid prefix", request.RequestID)
		}
		byID[request.RequestID] = request
	}
	for _, record := range records {
		request, exists := byID[record.RequestID]
		if !exists {
			return Report{}, fmt.Errorf("cachebench: observation request %q absent from trace", record.RequestID)
		}
		if record.Provider != request.Provider || record.Epoch != request.Epoch {
			return Report{}, fmt.Errorf("cachebench: observation request %q provider/epoch mismatch", record.RequestID)
		}
		if record.RequestBodySHA256 == "" || record.RequestBodySHA256 != request.BodySHA256 {
			return Report{}, fmt.Errorf("cachebench: observation request %q body digest mismatch", record.RequestID)
		}
	}
	report, err := EvaluateObserved(records, target)
	if err != nil {
		return Report{}, err
	}
	report.EvidenceLimitations = append([]string{
		"observation population exactly joined to supplied request trace by request ID and body SHA-256",
	}, report.EvidenceLimitations...)
	return report, nil
}

// ObservationReadLimits bounds JSONL evidence decoding.
type ObservationReadLimits struct {
	MaxLineBytes int

View on GitHub (pinned to 27d5a3981a)

Solutions

  1. Regenerate both trace and observations within the same epoch.
  2. Make provider strings identical (same casing/alias) in both pipelines.
  3. If epochs were renamed intentionally, regenerate the trace under the new epoch label.
Defensive patterns

Strategy: validation

Validate before calling

func sameContext(records []ObservationRecord, trace []TraceRecord) error {
	byID := map[string]TraceRecord{}
	for _, t := range trace {
		byID[t.RequestID] = t
	}
	for _, r := range records {
		if t, ok := byID[r.RequestID]; ok && (r.Provider != t.Provider || r.Epoch != t.Epoch) {
			return fmt.Errorf("observation %q provider/epoch mismatch", r.RequestID)
		}
	}
	return nil
}

Prevention

When it happens

Trigger: EvaluateObservedAgainstTrace joining record to trace where record.Provider != request.Provider or record.Epoch != request.Epoch — commonly observations captured in one epoch being evaluated against a trace from another.

Common situations: Re-running only the observation phase under a new epoch label while reusing an old trace; provider string differing in case or alias between capture and trace generation.

Related errors


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