JuliusBrussee/caveman · error

start experiment %q: %w

Error message

start experiment %q: %w

What it means

StartExperiment's INSERT into the experiments table failed; the message wraps the database error. Inputs already passed validation, so this is a storage-level failure such as lock contention or constraint violation.

Source

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

	SavedUSD   *float64              `json:"saved_usd_per_session,omitempty"`
	Currency   string                `json:"currency,omitempty"`
	Attributed LearnAttribution      `json:"attribution"`
	Caveats    []string              `json:"caveats"`
}

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 {

View on GitHub (pinned to 81536f57b3)

Solutions

  1. Inspect the wrapped error (constraint, lock, closed handle)
  2. Retry after transient SQLite contention clears
  3. Verify schema migrations ran on the store before starting experiments
Defensive patterns

Strategy: retry

When it happens

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