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
- Inspect the wrapped error (constraint, lock, closed handle)
- Retry after transient SQLite contention clears
- 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.