{"record":{"id":"a58facaad22c1ff8","repo":"JuliusBrussee/caveman","slug":"stop-experiment-w","errorCode":null,"errorMessage":"stop experiment: %w","messagePattern":"stop experiment: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"proxy/internal/store/learn_experiment.go","lineNumber":155,"sourceCode":"\tif _, err := s.db.Exec(`INSERT INTO experiment_arms (experiment_id, arm, started_at)\n\t\tVALUES (?, ?, ?)`, experiment.id, arm, now); err != nil {\n\t\treturn Experiment{}, fmt.Errorf(\"open arm %q: %w\", arm, err)\n\t}\n\treturn s.LoadExperiment(label)\n}\n\nfunc (s *Store) StopExperiment(label string) (Experiment, error) {\n\texperiment, err := s.LoadExperiment(label)\n\tif err != nil {\n\t\treturn Experiment{}, err\n\t}\n\tnow := learnOutcomeClock().UTC().Format(time.RFC3339)\n\tif _, err := s.db.Exec(`UPDATE experiment_arms SET ended_at = ?\n\t\tWHERE experiment_id = ? AND ended_at IS NULL`, now, experiment.id); err != nil {\n\t\treturn Experiment{}, fmt.Errorf(\"close open arm: %w\", err)\n\t}\n\tif _, err := s.db.Exec(`UPDATE experiments SET stopped_at = ? WHERE id = ?`, now, experiment.id); err != nil {\n\t\treturn Experiment{}, fmt.Errorf(\"stop experiment: %w\", err)\n\t}\n\treturn s.LoadExperiment(label)\n}\n\nfunc (s *Store) LoadExperiment(label string) (Experiment, error) {\n\tlabel = strings.TrimSpace(label)\n\trow := s.db.QueryRow(`SELECT id, label, COALESCE(sink_id,''), COALESCE(fix_kind,''),\n\t\tCOALESCE(note,''), created_at, COALESCE(stopped_at,'') FROM experiments WHERE label = ?`, label)\n\tvar experiment Experiment\n\tif err := row.Scan(&experiment.id, &experiment.Label, &experiment.SinkID, &experiment.FixKind,\n\t\t&experiment.Note, &experiment.CreatedAt, &experiment.StoppedAt); err != nil {\n\t\tif err == sql.ErrNoRows {\n\t\t\treturn Experiment{}, fmt.Errorf(\"no experiment named %q\", label)\n\t\t}\n\t\treturn Experiment{}, err\n\t}\n\trows, err := s.db.Query(`SELECT arm, started_at, COALESCE(ended_at,'')\n\t\tFROM experiment_arms WHERE experiment_id = ? ORDER BY started_at, id`, experiment.id)","sourceCodeStart":137,"sourceCodeEnd":173,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/proxy/internal/store/learn_experiment.go#L137-L173","documentation":"StopExperiment wraps the error from the UPDATE that sets experiments.stopped_at. This library throws it when finalizing the experiment row fails; note the arms were already closed by the preceding UPDATE, so a failure here leaves arms closed but stopped_at unset.","triggerScenarios":"Calling Store.StopExperiment(label) when 'UPDATE experiments SET stopped_at = ? WHERE id = ?' fails: database locked, missing experiments table, disk error.","commonSituations":"Concurrent write contention on SQLite; schema drift; the DB file becoming read-only mid-operation.","solutions":["Unwrap the error to see the underlying SQLite cause.","Retry after resolving lock contention (busy_timeout).","Confirm experiments table exists with a nullable stopped_at column.","If stuck with closed arms but no stopped_at, re-run StopExperiment — it is idempotent for the arm-closing step."],"exampleFix":"// before\nif _, err := s.db.Exec(`UPDATE experiments SET stopped_at = ? WHERE id = ?`, now, experiment.id); err != nil {\n    return Experiment{}, fmt.Errorf(\"stop experiment: %w\", err)\n}\n// after\n// same code; caller-side retry on busy:\nif _, err := store.StopExperiment(label); err != nil {\n    if isBusy(err) { time.Sleep(time.Second); _, err = store.StopExperiment(label) }\n    return err\n}","handlingStrategy":"retry","validationCode":"var stopped string\n_ = s.db.QueryRow(`SELECT COALESCE(stopped_at,'') FROM experiments WHERE label = ?`, label).Scan(&stopped) // existence + state check","typeGuard":null,"tryCatchPattern":"_, err := store.StopExperiment(label)\nif err != nil {\n    if isBusy(err) { /* retry */ }\n    return fmt.Errorf(\"stop: %w\", err)\n}","preventionTips":["Retry idempotently — StopExperiment is safe to re-run","Verify experiments table schema","Avoid concurrent stops"],"tags":["go","database","sqlite","experiment"],"backgroundTag":"database-write-failed","analyzedSha":"3ee70a102609e550bd2e68004bf5990a9341c851","analyzedAt":"2026-09-20T15:53:39.229Z","contentChangedAt":"2026-09-20T15:53:39.229Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}