hiyouga/LlamaFactory · error · ValueError

`dynamic_batching` requires `max_steps` because it is step-d

Error message

`dynamic_batching` requires `max_steps` because it is step-driven.

What it means

In v1 `TrainingArguments.__post_init__`, choosing `batching_strategy: dynamic_batching` requires `max_steps` to be a positive integer because dynamic batching has no fixed epoch boundary — training length is driven purely by optimizer steps. Without `max_steps` the trainer could not know when to stop or schedule.

Source

Thrown at src/llamafactory/v1/config/training_args.py:196

            register_deepspeed_dist_config(self.dist_config)
        except ImportError:
            pass

        # The optimizer learning rate has a single source of truth: ``learning_rate``.
        # Propagate it into ``optim_config["lr"]`` so optimizer plugins (e.g. Muon) pick it up
        # via ``optim_config.get("lr")`` without each plugin needing a separate ``learning_rate`` arg.
        if self.optim_config is not None:
            if "lr" in self.optim_config:
                logger.warning_rank0(
                    "`optim_config.lr` is overridden by `learning_rate`; set the learning rate via "
                    "`learning_rate` instead and remove `lr` from `optim_config`."
                )
            self.optim_config["lr"] = self.learning_rate

        if str(self.batching_strategy) == str(BatchingStrategy.DYNAMIC_BATCHING):
            if self.max_steps is None or self.max_steps <= 0:
                raise ValueError("`dynamic_batching` requires `max_steps` because it is step-driven.")
            if self.save_epochs is not None:
                raise ValueError("`save_epochs` is not supported with `dynamic_batching`; use `save_steps` instead.")

View on GitHub (pinned to f28afaf635)

Solutions

  1. Set an explicit positive `max_steps` appropriate for your token budget (e.g. `max_steps: 1000`)
  2. Replace `epochs`-based stopping with `max_steps` when using dynamic batching
  3. If you need epoch semantics, keep a static/padding strategy instead of `dynamic_batching`

Example fix

# before (yaml)
training:
  batching_strategy: dynamic_batching
  epochs: 3

# after (yaml)
training:
  batching_strategy: dynamic_batching
  max_steps: 1000
Defensive patterns

Strategy: validation

Validate before calling

def validate_dynamic_batching(cfg: dict) -> None:
    if str(cfg.get("batching_strategy", "")) == "dynamic_batching":
        steps = cfg.get("max_steps")
        if steps is None or steps <= 0:
            raise SystemExit("dynamic_batching requires positive max_steps")

Prevention

When it happens

Trigger: Setting `batching_strategy: dynamic_batching` in v1 training args while `max_steps` is None (default) or <= 0, typically because the config relies on `epochs` instead.

Common situations: Switching an epoch-based config to dynamic batching without adding a step budget; setting `max_steps: -1` (HF convention for 'unset') which fails the <= 0 check.

Related errors


AI-assisted analysis of hiyouga/LlamaFactory@f28afaf635 (2026-08-14). Data as JSON: /api/errors/e95bbdc55a6ecd1a. Report an issue: GitHub.