huggingface/transformers · error · ValueError

Can only set a dictionary as `pp_plan`

Error message

Can only set a dictionary as `pp_plan`

What it means

The pp_plan setter only accepts None (which resets the plan to an empty dict) or a dictionary mapping layer patterns to (input_module_id, output_module_id) tuples. Passing any other type (a string, list, or a plan object) raises this ValueError immediately, before any pipeline-parallel setup runs.

Source

Thrown at src/transformers/distributed/mixin.py:146

            for param_name in model_param_names:
                if re.match(regex_pattern, param_name):
                    pattern_matched = True
                    break
            if not pattern_matched:
                warnings.warn(
                    f"Layer pattern '{layer_pattern}' does not match any parameters in the model. This rule may not "
                    "be applied during tensor parallelization, or may lead to dimension mismatches"
                )

        self._tp_plan = plan

    @pp_plan.setter
    def pp_plan(self, plan: dict[str, tuple[str, str]] | None):
        if plan is None:
            self._pp_plan = {}
            return
        if not isinstance(plan, dict):
            raise ValueError("Can only set a dictionary as `pp_plan`")

        self._pp_plan = plan

    @classmethod
    def prepare_distribute_model(
        cls,
        distributed_config: DistributedConfig | dict | None,
        *,
        device_mesh=None,
        device_map=None,
    ) -> tuple[DistributedConfig | None, object, object]:
        if distributed_config is None:
            return None, device_map, device_mesh

        if isinstance(distributed_config, dict):
            distributed_config = DistributedConfig.from_dict(distributed_config)

        if distributed_config.tp_size > 1 or distributed_config.fsdp_size > 1:

View on GitHub (pinned to a597f97485)

Solutions

  1. Pass a dict of the form {layer_pattern: (input_placeholder_id, output_placeholder_id)} or None.
  2. If you do not want pipeline parallelism, set pp_plan = None (or omit it) instead of an empty string.
  3. Check that YAML/JSON config loading produced a dict, not a list of pairs.

Example fix

# before
model.pp_plan = "auto"

# after
model.pp_plan = {"model.layers.*": ("model.embed_tokens", "lm_head")}
Defensive patterns

Strategy: type-guard

Validate before calling

def validate_pp_plan(plan) -> None:
    if plan is None:
        return
    if not isinstance(plan, dict):
        raise TypeError("pp_plan must be a dict[layer_pattern, tuple[str, str]] or None")
    for k, v in plan.items():
        if not (isinstance(v, tuple) and len(v) == 2 and all(isinstance(x, str) for x in v)):
            raise ValueError(f"pp_plan['{k}'] must be (input_module_id, output_module_id)")

Type guard

def is_valid_pp_plan(plan: object) -> bool:
    if plan is None:
        return True
    return isinstance(plan, dict) and all(
        isinstance(v, tuple) and len(v) == 2 and all(isinstance(s, str) for s in v)
        for v in plan.values()
    )

Try / catch

try:
    model.pp_plan = plan
except ValueError as e:
    if "Can only set a dictionary" in str(e):
        raise TypeError(f"pp_plan must be a dict, got {type(plan).__name__}") from e
    raise

Prevention

When it happens

Trigger: model.pp_plan = 'auto' or model.pp_plan = [('layers.0', (0, 0)), ...] — anything that is not a dict[str, tuple[str, str]] and not None.

Common situations: Confusing pp_plan with tp_plan, which additionally accepts the string 'auto'; passing a parsed YAML/JSON value that became a list; passing a PipelineParallelismPlan-like object instead of the raw dict.

Related errors


AI-assisted analysis of huggingface/transformers@a597f97485 (2026-08-14). Data as JSON: /api/errors/9c04fc2c1fb4fa52. Report an issue: GitHub.