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
- Pass a dict of the form {layer_pattern: (input_placeholder_id, output_placeholder_id)} or None.
- If you do not want pipeline parallelism, set pp_plan = None (or omit it) instead of an empty string.
- 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
- pp_plan has no 'auto' string — only dict or None.
- Type your config fields (dict[str, tuple[str, str]] | None) so mypy catches mistakes.
- Validate YAML-loaded configs before assigning.
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
- out_indices must be a list, got {type(self._out_indices)}
- You can only update int, float, bool or string values in the
- Unsupported tensor parallel style '{parallel_style}' for lay
- tp_size ({distributed_config.tp_size}) * fsdp_size ({distrib
- save_pretrained(..., distributed_checkpoint=True) is only su
AI-assisted analysis of huggingface/transformers@a597f97485 (2026-08-14).
Data as JSON: /api/errors/9c04fc2c1fb4fa52.
Report an issue: GitHub.