JuliusBrussee/caveman · error

read experiment id: %w

Error message

read experiment id: %w

What it means

StartExperiment could not read the auto-increment id after inserting the experiment row (LastInsertId failed). The row may exist without its id being retrievable — a driver/store-level failure, not a validation problem.

Source

Thrown at proxy/internal/store/learn_experiment.go:103

}

func (s *Store) StartExperiment(label, sinkID, fixKind, note string) (Experiment, error) {
	label = strings.TrimSpace(label)
	if label == "" || len(label) > 128 {
		return Experiment{}, fmt.Errorf("experiment label must be 1-128 bytes")
	}
	if len(note) > 4096 {
		return Experiment{}, fmt.Errorf("note exceeds 4096 bytes")
	}
	now := learnOutcomeClock().UTC().Format(time.RFC3339)
	result, err := s.db.Exec(`INSERT INTO experiments (label, sink_id, fix_kind, note, created_at)
		VALUES (?, ?, ?, ?, ?)`, label, sinkID, fixKind, note, now)
	if err != nil {
		return Experiment{}, fmt.Errorf("start experiment %q: %w", label, err)
	}
	id, err := result.LastInsertId()
	if err != nil {
		return Experiment{}, fmt.Errorf("read experiment id: %w", err)
	}
	if _, err := s.db.Exec(`INSERT INTO experiment_arms (experiment_id, arm, started_at)
		VALUES (?, ?, ?)`, id, experimentArmOn, now); err != nil {
		return Experiment{}, fmt.Errorf("open first arm: %w", err)
	}
	return s.LoadExperiment(label)
}

// SwitchExperimentArm closes the open arm and opens the other one. Switching to
// the arm already running is a no-op rather than an error: it keeps a repeated
// call from fragmenting the interval history.
func (s *Store) SwitchExperimentArm(label, arm string) (Experiment, error) {
	arm = strings.ToLower(strings.TrimSpace(arm))
	if arm != experimentArmOn && arm != experimentArmOff {
		return Experiment{}, fmt.Errorf("arm must be %q or %q", experimentArmOn, experimentArmOff)
	}
	experiment, err := s.LoadExperiment(label)
	if err != nil {

View on GitHub (pinned to 81536f57b3)

Solutions

  1. Retry the experiment start; check whether the orphaned row was inserted and clean it up
  2. Inspect the wrapped driver error for a closed or locked database
  3. Query the experiments table by label to recover the id of an already-inserted row
Defensive patterns

Strategy: retry

When it happens

Trigger: Thrown at proxy/internal/store/learn_experiment.go:103 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of JuliusBrussee/caveman@81536f57b3 (2026-08-27). Data as JSON: /api/errors/760f242020e98696. Report an issue: GitHub.