github/scientist · error · Scientist::BehaviorNotUnique
# already has # behavior
Error message
#{experiment.name} already has #{name} behavior What it means
Scientist::BehaviorNotUnique is raised by Experiment#try when a behavior with the same name has already been registered on the experiment. Each experiment instance allows exactly one block per behavior name ("control" for `use`, "candidate" by default for `try`); duplicate registration raises instead of silently overwriting.
Solutions
- Use distinct names for each candidate: `try("candidate_a") { }`, `try("candidate_b") { }`.
- Ensure each registration method is called at most once per name per instance; guard with `behaviors.include?(name)` if unsure.
- Create a new experiment instance if you need to re-register the same-named behavior.
- Fix the code path that invokes the registration helper twice (e.g. a loop not creating fresh experiments per iteration).
Example fix
# before
exp.try { candidate_a } # registers "candidate"
exp.try { candidate_b } # BehaviorNotUnique
# after
exp.try("candidate_a") { candidate_a }
exp.try("candidate_b") { candidate_b }
exp.run Defensive patterns
Strategy: validation
Validate before calling
raise "duplicate candidate #{name}" if experiment.send(:behaviors).key?(name.to_s)
experiment.try(name) { block } Type guard
def can_register?(exp, name = "candidate") !exp.send(:behaviors).key?(name.to_s) end
Try / catch
begin
experiment.try(name) { candidate_block }
rescue Scientist::BehaviorNotUnique
logger.warn("candidate #{name} already registered; skipping")
end Prevention
- Give every `try` call an explicit unique name instead of relying on the default "candidate".
- Call `use`/`try` exactly once per name; build registration in one place, not in loops over the same instance.
- Create a fresh experiment object per registration cycle instead of reusing one.
- In helpers, check `behaviors.include?(name)` before registering.
When it happens
Trigger: Calling `experiment.try { ... }` twice without names (both register "candidate"); calling `experiment.try(:x) { }` twice; calling `use` twice (both register "control"); re-running a behavior-registration helper on the same experiment instance; named `try` calls that collide with an existing name.
Common situations: Loops or helper methods that register candidates and get invoked twice on the same experiment object; duplicate `use` calls from copy-paste; memoization mistakes that re-run registration code; framework integrations registering a behavior in both a DSL callback and setup code.
Understand the failure class
Background: "already exists" / EEXIST / FileAlreadyExistsException: what the 'file already exists' error means and how to fix it — this error's family across 37 libraries.
Related errors
AI-assisted analysis of github/scientist@504a396e98 (2026-09-13).
Data as JSON: /api/errors/ed3ac0aef38e8b44.
Report an issue: GitHub.
Appendix: source
Thrown at lib/scientist/experiment.rb:296
return false
end
# Internal: determine whether or not an experiment should run.
#
# Rescues and reports exceptions in the enabled method if they occur.
def should_experiment_run?
behaviors.size > 1 && enabled? && run_if_block_allows?
rescue StandardError => ex
raised :enabled, ex
return false
end
# Register a named behavior for this experiment, default "candidate".
def try(name = nil, &block)
name = (name || "candidate").to_s
if behaviors.include?(name)
raise Scientist::BehaviorNotUnique.new(self, name)
end
behaviors[name] = block
end
# Register the control behavior for this experiment.
def use(&block)
try "control", &block
end
# Whether or not to raise a mismatch error when a mismatch occurs.
def raise_on_mismatches?
if raise_on_mismatches.nil?
self.class.raise_on_mismatches?
else
!!raise_on_mismatches
end
endView on GitHub (pinned to 504a396e98)