hiyouga/LlamaFactory · error · TypeError

{cls.__name__} config must be a mapping or {params_cls.__nam

Error message

{cls.__name__} config must be a mapping or {params_cls.__name__}, got {type(config).__name__}.

What it means

TypeError from PluginBase.parse_params (plugin.py:83): the plugin config argument is neither None, a dict, nor an instance of the expected params dataclass, so it cannot be converted. Examples: passing a string, a list, a dataclass of the wrong type, or another plugin's params object. The check runs before field validation, so unknown-key errors (raised just below) never fire for these inputs.

Source

Thrown at src/llamafactory/v1/utils/plugin.py:83

        def decorator(obj: Any) -> Any:
            cls._registry[self.name] = obj
            return obj

        return decorator

    @classmethod
    def parse_params(cls, config: Any, params_cls: type[ParamsT]) -> ParamsT:
        """Strictly convert config to the params dataclass used by one plugin entrypoint."""
        if not is_dataclass(params_cls):
            raise TypeError(f"{cls.__name__} params must be a dataclass type, got {params_cls!r}.")
        if isinstance(config, params_cls):
            return config
        if config is None:
            values = {}
        elif isinstance(config, dict):
            values = dict(config)
        else:
            raise TypeError(
                f"{cls.__name__} config must be a mapping or {params_cls.__name__}, got {type(config).__name__}."
            )

        known = {item.name for item in fields(params_cls)}
        unknown = set(values) - known
        if unknown:
            raise ValueError(
                f"Unknown params for {cls.__name__}.{params_cls.__name__}: {sorted(unknown)}. "
                f"Expected: {sorted(known)}"
            )

        return params_cls(**values)

    def _resolve(self) -> Any:
        cls = type(self)
        if self.name is None:
            raise ValueError(f"{cls.__name__} must be constructed with a name.")
        if self.name not in cls._registry:

View on GitHub (pinned to f28afaf635)

Solutions

  1. Pass a dict of parameters (or None for defaults): DistributedPlugin.parse_params({'ep_size': 2}, FSDP2Params).
  2. Pass an instance of exactly the params_cls the entrypoint expects, not a sibling dataclass.
  3. If the config comes from YAML/JSON, ensure it deserializes to a mapping at the plugin-config level.
  4. For programmatic construction, keep types consistent: build params via parse_params rather than hand-instantiating foreign dataclasses.

Example fix

# before
plugin.shard_model(model, dist_config="fsdp2")

# after
plugin.shard_model(model, dist_config={"name": "fsdp2", "ep_size": 2})
Defensive patterns

Strategy: type-guard

Validate before calling

assert dist_config is None or isinstance(dist_config, dict) or isinstance(dist_config, FSDP2Params), \
    f"dist_config must be a mapping or params dataclass, got {type(dist_config).__name__}"

Type guard

def is_plugin_config(config, params_cls) -> bool:
    return config is None or isinstance(config, dict) or isinstance(config, params_cls)

Prevention

When it happens

Trigger: Calling a plugin entrypoint with dist_config='fsdp2' (string name instead of a mapping), a FSDP2Params passed to a DeepSpeed plugin, or an object created by a different library version.

Common situations: Passing the plugin name string directly instead of {name: ..., ...}; reusing params objects across plugin families; configs loaded from YAML as lists instead of mappings; version skew where a persisted params dataclass no longer matches.

Related errors


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