github/scientist · error · Scientist::BehaviorMissing
# missing # behavior
Error message
#{experiment.name} missing #{name} behavior What it means
Scientist::BehaviorMissing is raised by Experiment#run when the behavior name passed to `run(name)` (defaulting to "control") is not present in the experiment's registered behaviors hash. An experiment can only run a behavior registered via `use`/`try`; this error guards against running an experiment whose required control (or named) behavior was never defined.
Solutions
- Register the control behavior with `use { ... }` before calling `run`.
- Make the name passed to `run(name)` exactly match a name passed to `try(name)` (both are to_s'd, so 'x' and :x are equivalent).
- Register behaviors unconditionally on every code path that reaches `run`, or check that the behavior exists before running.
- Verify you are running the same experiment instance on which the behaviors were registered.
Example fix
# before
experiment = MyExperiment.new
experiment.try("new_code") { compute_v2 }
experiment.run # raises BehaviorMissing (no control)
# after
experiment = MyExperiment.new
experiment.use { compute_v1 }
experiment.try("new_code") { compute_v2 }
experiment.run # returns control value Defensive patterns
Strategy: validation
Validate before calling
def behavior_registered?(exp, name = "control") exp.send(:behaviors).key?(name.to_s) end raise "missing control behavior" unless behavior_registered?(experiment)
Type guard
def behavior_registered?(exp, name = "control") exp.respond_to?(:run) && exp.send(:behaviors).key?(name.to_s) rescue NoMethodError false end
Try / catch
begin
result = experiment.run("control")
rescue Scientist::BehaviorMissing => e
logger.warn("scientist behavior missing: #{e.message}")
fallback_compute
end Prevention
- Always call `use` (control) before `run`, unconditionally on every code path.
- Match `run(name)` arguments exactly to a `try(name)` registration; use string names consistently.
- Keep behavior registration together in one setup method so omissions are visible in review.
- Add a test asserting every experiment registers its control before running.
When it happens
Trigger: Calling `experiment.run` when no `use`/control behavior was registered, or calling `experiment.run("candidate")` where no `try("candidate")` with that name was registered. Also triggered by a name typo/mismatch between `run(name)` and `try(name)`, or by registering behaviors on a different experiment instance (behaviors are per-instance and frozen at run time).
Common situations: Refactors that rename a candidate but forget to update the `run` call; experiments where `try`/`use` is called conditionally so the control is never registered on some code paths; copy-pasted experiment definitions missing the `use` block.
Understand the failure class
Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.
Related errors
AI-assisted analysis of github/scientist@504a396e98 (2026-09-13).
Data as JSON: /api/errors/8167734f690120c1.
Report an issue: GitHub.
Appendix: source
Thrown at lib/scientist/experiment.rb:230
# Called when an exception is raised while running an internal operation,
# like :publish. Override this method to track these exceptions. The
# default implementation re-raises the exception.
def raised(operation, error)
raise error
end
# Internal: Run all the behaviors for this experiment, observing each and
# publishing the results. Return the result of the named behavior, default
# "control".
def run(name = nil)
behaviors.freeze
context.freeze
name = (name || "control").to_s
block = behaviors[name]
if block.nil?
raise Scientist::BehaviorMissing.new(self, name)
end
unless should_experiment_run?
return block.call
end
if @_scientist_before_run
@_scientist_before_run.call
end
result = generate_result(name)
if @_scientist_after_run
@_scientist_after_run.call(result)
end
begin
publish(result)View on GitHub (pinned to 504a396e98)