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

  1. Register the control behavior with `use { ... }` before calling `run`.
  2. 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).
  3. Register behaviors unconditionally on every code path that reaches `run`, or check that the behavior exists before running.
  4. 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

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)