JuliusBrussee/caveman · error

cachebench: replay preparation interrupted: %w

Error message

cachebench: replay preparation interrupted: %w

What it means

The context passed to ReplayRunner.prepare was canceled or expired while it was looping over trace records preparing optimized bodies. Preparation checks ctx.Err() between records and wraps it with this message, so no partial results are used.

Source

Thrown at cacheengine/cachebench/replay.go:505

	}
	anchorTrace, _ := time.Parse(time.RFC3339Nano, records[0].At)
	anchorReal := now().UTC()
	if runner.Limits.MaxConcurrency == 1 {
		return runner.runSequential(ctx, prepared, anchorTrace, anchorReal, now, sleep, emit)
	}
	return runner.runConcurrent(ctx, prepared, anchorTrace, anchorReal, now, sleep, emit)
}

type preparedReplay struct {
	record    TraceRecord
	optimized cacheengine.NativeResult
}

func (runner ReplayRunner) prepare(ctx context.Context, records []TraceRecord) ([]preparedReplay, error) {
	prepared := make([]preparedReplay, 0, len(records))
	for _, record := range records {
		if err := ctx.Err(); err != nil {
			return nil, fmt.Errorf("cachebench: replay preparation interrupted: %w", err)
		}
		native, err := record.NativeRequest()
		if err != nil {
			return nil, err
		}
		optimized, err := runner.Engine.Optimize(ctx, native)
		if err != nil {
			return nil, fmt.Errorf("cachebench: optimize request %q: %w", record.RequestID, err)
		}
		equivalent := bytes.Equal(native.Body, optimized.Body)
		if optimized.Applied {
			equivalent = ModelVisibleEquivalent(native.Body, optimized.Body)
		}
		if !equivalent {
			return nil, &ReplayRunError{
				RequestID: record.RequestID, FailureCode: "model_visible_mismatch",
				Err: fmt.Errorf("cachebench: request %q failed model-visible equivalence", record.RequestID),
			}

View on GitHub (pinned to 27d5a3981a)

Solutions

  1. Check what canceled the context: errors.Is(err, context.DeadlineExceeded) vs context.Canceled tells you timeout vs explicit cancel.
  2. Raise or remove the deadline on the Run/prepare context for large traces.
  3. If cancellation was intentional (Ctrl+C, shutdown), treat this as a clean abort and discard partial state.
  4. Speed up preparation (fewer records, faster Engine) so the window for cancellation shrinks.

Example fix

// before
ctx, cancel := context.WithTimeout(ctx, 5*time.Second) // prepare of 10k records exceeds it

// after
ctx, cancel := context.WithTimeout(ctx, 10*time.Minute)
defer cancel()
Defensive patterns

Strategy: try-catch

Validate before calling

if err := ctx.Err(); err != nil {
    return fmt.Errorf("refusing to start replay: %w", err)
}

Try / catch

if err := runner.Run(ctx, records, emit); err != nil {
    if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {
        // treat as aborted run; persist already-emitted results and exit cleanly
    }
    return err
}

Prevention

When it happens

Trigger: Canceling the parent context (ctx cancel, timeout on the Run call) while prepare is iterating records; a deadline shorter than the time Engine.Optimize needs for the whole trace; SIGINT handlers that cancel the run context.

Common situations: A command wrapper with a --timeout flag that fires during long replays; test code with context.WithTimeout too tight for large traces; user Ctrl+C mid-run propagating a cancel.

Related errors


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