JuliusBrussee/caveman · error

open first arm: %w

Error message

open first arm: %w

What it means

SQLite insert of the initial experiments row failed while starting a learn experiment, so the experiment could not be created at all. It fires only on a database-level failure (locked DB, disk error, schema mismatch), not on invalid input, which is validated earlier.

Source

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

	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 {
		return Experiment{}, err
	}
	if experiment.StoppedAt != "" {
		return Experiment{}, fmt.Errorf("experiment %q is stopped", experiment.Label)

View on GitHub (pinned to 81536f57b3)

Solutions

  1. Check the SQLite database file is writable and not locked by another process
  2. Inspect the wrapped underlying driver error for the real cause (e.g. disk I/O, corruption)
  3. Retry StartExperiment once the database is available
  4. Verify the experiments table schema matches the INSERT column list
Defensive patterns

Strategy: retry

When it happens

Trigger: Thrown at proxy/internal/store/learn_experiment.go:107 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/b2142f78c6f7d922. Report an issue: GitHub.