huggingface/smolagents · error · ValueError

step_callbacks must be a list or a dict

Error message

step_callbacks must be a list or a dict

What it means

The step_callbacks parameter of an agent must be either a list (applied to all step types) or a dict mapping step classes to callback(s). Anything else (a string, a bare function, a tuple, None passed explicitly with wrong type) raises ValueError('step_callbacks must be a list or a dict').

Source

Thrown at src/smolagents/agents.py:432

            )

    def _setup_step_callbacks(self, step_callbacks):
        # Initialize step callbacks registry
        self.step_callbacks = CallbackRegistry()
        if step_callbacks:
            # Register callbacks list only for ActionStep for backward compatibility
            if isinstance(step_callbacks, list):
                for callback in step_callbacks:
                    self.step_callbacks.register(ActionStep, callback)
            # Register callbacks dict for specific step classes
            elif isinstance(step_callbacks, dict):
                for step_cls, callbacks in step_callbacks.items():
                    if not isinstance(callbacks, list):
                        callbacks = [callbacks]
                    for callback in callbacks:
                        self.step_callbacks.register(step_cls, callback)
            else:
                raise ValueError("step_callbacks must be a list or a dict")
        # Register monitor update_metrics only for ActionStep for backward compatibility
        self.step_callbacks.register(ActionStep, self.monitor.update_metrics)

    def run(
        self,
        task: str,
        stream: bool = False,
        reset: bool = True,
        images: list["PIL.Image.Image"] | None = None,
        additional_args: dict | None = None,
        max_steps: int | None = None,
        return_full_result: bool | None = None,
    ) -> Any | RunResult:
        """
        Run the agent for the given task.

        Args:
            task (`str`): Task to perform.

View on GitHub (pinned to 30bb116109)

Solutions

  1. Wrap a single callback in a list: step_callbacks=[my_callback]
  2. For per-step-type callbacks use a dict: {ActionStep: [cb1], PlanningStep: cb2}
  3. If building callbacks dynamically, verify isinstance(cb, (list, dict)) before construction

Example fix

# before
agent = CodeAgent(tools=[], llm_engine=llm, step_callbacks=my_callback)

# after
agent = CodeAgent(tools=[], llm_engine=llm, step_callbacks=[my_callback])
Defensive patterns

Strategy: type-guard

Validate before calling

cb = my_callback if isinstance(my_callback, (list, dict)) else [my_callback]
agent = CodeAgent(tools=[], llm_engine=llm, step_callbacks=cb)

Type guard

def normalize_step_callbacks(cb):
    if isinstance(cb, dict):
        return {k: (v if isinstance(v, list) else [v]) for k, v in cb.items()}
    if callable(cb):
        return [cb]
    if isinstance(cb, list):
        return cb
    raise TypeError("step_callbacks must be a list or a dict")

Prevention

When it happens

Trigger: Passing step_callbacks=some_function or step_callbacks=(cb1, cb2) or step_callbacks="callback" instead of a list/dict; a single callable is not accepted and must be wrapped in a list.

Common situations: Intuitively passing one callback function directly; passing a tuple because other libraries accept iterables; migrating code that previously used the older single-callback signature.

Related errors


AI-assisted analysis of huggingface/smolagents@30bb116109 (2026-08-28). Data as JSON: /api/errors/f0035839cdbf2632. Report an issue: GitHub.