hiyouga/LlamaFactory · error · ValueError

`save_epochs` is not supported with `dynamic_batching`; use

Error message

`save_epochs` is not supported with `dynamic_batching`; use `save_steps` instead.

What it means

v1 `TrainingArguments.__post_init__` rejects `save_epochs` together with `batching_strategy: dynamic_batching`. Dynamic batching is step-driven and never computes epoch boundaries, so epoch-conditional checkpointing is meaningless; `save_steps` must be used for periodic checkpoints instead.

Source

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

        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. Delete `save_epochs` and set `save_steps` (e.g. `save_steps: 500`)
  2. If epoch checkpoints are a hard requirement, switch back to a non-dynamic batching strategy and use `max_steps`/`epochs` normally

Example fix

# before (yaml)
training:
  batching_strategy: dynamic_batching
  max_steps: 1000
  save_epochs: 1

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

Strategy: validation

Validate before calling

def validate_checkpointing(cfg: dict) -> None:
    if str(cfg.get("batching_strategy", "")) == "dynamic_batching" and cfg.get("save_epochs") is not None:
        raise SystemExit("use save_steps with dynamic_batching, not save_epochs")

Prevention

When it happens

Trigger: Configuring `batching_strategy: dynamic_batching` plus a non-null `save_epochs` in the v1 training args.

Common situations: Copying a checkpointing block (`save_epochs: 1`) from an epoch-based config into a dynamic-batching run.

Related errors


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