Lightning-AI/pytorch-lightning · error · TypeError
Blocking backward sync is only possible if the module passed
Error message
Blocking backward sync is only possible if the module passed to `{self.__class__.__name__}.no_backward_sync` is wrapped in `DistributedDataParallel`. Got: {module.__class__.__name__}. What it means
DDPStrategy.no_backward_sync(enabled=True) returns module.no_sync(), which only exists on torch's DistributedDataParallel wrapper. If the module you pass is a plain nn.Module (or the strategy hasn't wrapped it yet / you unwrapped it), a TypeError is raised telling you to pass the DDP-wrapped module.
Source
Thrown at src/lightning/fabric/strategies/ddp.py:268
self.cluster_environment.set_world_size(self.num_nodes * self.num_processes)
# `LightningEnvironment.set_global_rank` will do this too, but we cannot rely on that implementation detail
# additionally, for some implementations, the setter is a no-op, so it's safer to access the getter
rank_zero_only.rank = utils_rank_zero_only.rank = self.global_rank
def _determine_ddp_device_ids(self) -> Optional[list[int]]:
return None if self.root_device.type == "cpu" else [self.root_device.index]
class _DDPBackwardSyncControl(_BackwardSyncControl):
@override
def no_backward_sync(self, module: Module, enabled: bool) -> AbstractContextManager:
"""Blocks gradient synchronization inside the :class:`~torch.nn.parallel.distributed.DistributedDataParallel`
wrapper."""
if not enabled:
return nullcontext()
if not isinstance(module, DistributedDataParallel):
raise TypeError(
"Blocking backward sync is only possible if the module passed to"
f" `{self.__class__.__name__}.no_backward_sync` is wrapped in `DistributedDataParallel`."
f" Got: {module.__class__.__name__}."
)
return module.no_sync()
View on GitHub (pinned to 9fed5c27d2)
Solutions
- Pass the wrapped module — use fabric.no_backward_sync(...) as a context manager which uses the strategy's wrapped model, or ensure strategy.setup(module) ran first
- Only call no_backward_sync when enabled=True is needed on accumulation steps; the method returns nullcontext() when enabled=False, so gating it avoids the check entirely
- Ensure the strategy actually is DDPStrategy (single-device or deepspeed strategies don't produce a DDP wrapper)
Example fix
# before
with fabric.strategy.no_backward_sync(raw_module): # raw nn.Module
...
# after
with fabric.no_backward_sync(is_first_batch): # uses wrapped fabric.model
... Defensive patterns
Strategy: type-guard
Validate before calling
from torch.nn.parallel import DistributedDataParallel as DDP
if enabled and not isinstance(module, DDP):
module = fabric.strategy.model if isinstance(fabric.strategy.model, DDP) else module
# or skip no-sync on this step Type guard
from torch.nn.parallel import DistributedDataParallel as DDP
from contextlib import nullcontext
def safe_no_backward_sync(strategy, module, enabled: bool):
if not enabled or not isinstance(module, DDP):
return nullcontext()
return module.no_sync() Prevention
- Use fabric.no_backward_sync(...) which operates on the wrapped model
- Always run strategy.setup/fabric.setup before using sync control APIs
When it happens
Trigger: Calling fabric.strategy.no_backward_sync(module) or using the fabric.no_backward_sync context with gradient accumulation while the model is a raw nn.Module — e.g. before strategy.setup(module), after strategy.unwrap_module(model), or with a non-DDP strategy fallback.
Common situations: Gradient-accumulation loops that skip the no-sync context on the last batch; passing fabric.model after it was unwrapped for logging; calling no_backward_sync before setup wrapped the module in DistributedDataParallel.
Understand the failure class
Background: "Wrong argument type", "must be a string", "expected Array or Prism::Scope": TypeError and ArgumentError when a library receives a value of the wrong type — this error's family across 28 libraries.
Related errors
- `devices` selected with `CPUAccelerator` should be an int >
- The launcher can only create subprocesses once.
- Trying to inject a modified sampler into the batch sampler;
- Lightning can't inject a (distributed) sampler into your ba
- Expected a precision plugin, got {plugin}
AI-assisted analysis of Lightning-AI/pytorch-lightning@9fed5c27d2 (2026-08-28).
Data as JSON: /api/errors/75efde825de8eed0.
Report an issue: GitHub.