JuliusBrussee/caveman · error

arm must be %q or %q

Error message

arm must be %q or %q

What it means

Input validation guard in SwitchExperimentArm: the requested arm string was not one of the two allowed values (on/off) after lowercasing and trimming. It fires whenever a caller passes a typo, mixed-case value, or any arm name other than the two sentinel constants.

Source

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

	}
	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)
	}
	for _, existing := range experiment.Arms {
		if existing.EndedAt == "" && existing.Arm == arm {
			return experiment, nil
		}
	}
	now := learnOutcomeClock().UTC().Format(time.RFC3339)
	if _, err := s.db.Exec(`UPDATE experiment_arms SET ended_at = ?
		WHERE experiment_id = ? AND ended_at IS NULL`, now, experiment.id); err != nil {
		return Experiment{}, fmt.Errorf("close open arm: %w", err)
	}

View on GitHub (pinned to 81536f57b3)

Solutions

  1. Pass exactly "on" or "off" as the arm argument
  2. Normalize the arm string with strings.ToLower and strings.TrimSpace before calling
  3. Check the CLI flag or config value that supplied the arm name for typos
Defensive patterns

Strategy: validation

When it happens

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