{"record":{"id":"2bd8bceac8d33b7f","repo":"Lightning-AI/pytorch-lightning","slug":"it-is-recommended-to-use-self-log-result-metric","errorCode":null,"errorMessage":"It is recommended to use `self.log({result_metric.meta.name!r}, ..., sync_dist=True)` when logging on epoch level in distributed setting to accumulate the metric across devices.","messagePattern":"It is recommended to use `self\\.log\\((.+?), \\.\\.\\., sync_dist=True\\)` when logging on epoch level in distributed setting to accumulate the metric across devices\\.","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"src/lightning/pytorch/trainer/connectors/logger_connector/result.py","lineNumber":433,"sourceCode":"        self.update_metrics(key, value, batch_size)\n\n    @torch.compiler.disable\n    def update_metrics(self, key: str, value: _VALUE, batch_size: int) -> None:\n        result_metric = self[key]\n        # performance: avoid calling `__call__` to avoid the checks in `torch.nn.Module._call_impl`\n        result_metric.forward(value, batch_size)\n        result_metric.has_reset = False\n\n    @staticmethod\n    def _get_cache(result_metric: _ResultMetric, on_step: bool) -> Optional[Tensor]:\n        cache = None\n        if on_step and result_metric.meta.on_step:\n            cache = result_metric._forward_cache\n        elif not on_step and result_metric.meta.on_epoch:\n            if result_metric._computed is None:\n                should = result_metric.meta.sync.should\n                if not should and result_metric.is_tensor and _distributed_is_initialized():\n                    warning_cache.warn(\n                        f\"It is recommended to use `self.log({result_metric.meta.name!r}, ..., sync_dist=True)`\"\n                        \" when logging on epoch level in distributed setting to accumulate the metric across\"\n                        \" devices.\",\n                        category=PossibleUserWarning,\n                    )\n                result_metric.compute()\n                result_metric.meta.sync.should = should\n\n            cache = result_metric._computed\n\n        if cache is not None:\n            if not isinstance(cache, Tensor):\n                raise ValueError(\n                    f\"The `.compute()` return of the metric logged as {result_metric.meta.name!r} must be a tensor.\"\n                    f\" Found {cache}\"\n                )\n            if not result_metric.meta.enable_graph:\n                return cache.detach()","sourceCodeStart":415,"sourceCodeEnd":451,"githubUrl":"https://github.com/Lightning-AI/pytorch-lightning/blob/9fed5c27d2a62ff0efd6c3573599921d6ff67c14/src/lightning/pytorch/trainer/connectors/logger_connector/result.py#L415-L451","documentation":"PyTorch Lightning emits this warning when a metric logged via self.log(...) is reduced on epoch level (on_epoch=True) while running in a distributed setting (multi-GPU/multi-node) without sync_dist=True. Without sync_dist, each rank computes the metric only over its local shard of data and the epoch value is taken from one rank instead of being correctly accumulated/averaged across devices. The library warns because the resulting epoch-level metric can be silently wrong, not just suboptimal.","triggerScenarios":"Calling self.log('metric', value, on_step=False, on_epoch=True) (or a Metric object with on_epoch set) without sync_dist=True, while torch.distributed is initialized (ddp, ddp_spawn, deepspeed, fsdp strategies with >1 process). The check fires in _get_cache when the cached metric needs to be computed for the epoch and result_metric.meta.sync.should is False and the value is a tensor.","commonSituations":"Switching a single-GPU training script to DDP/multi-node without revisiting self.log calls; logging torchmetrics objects that default sync_dist=False; using reduce_fx='mean' on epoch level assuming Lightning already synchronizes; DeepSpeed/FSDP runs where per-rank losses differ and the reported epoch loss looks off.","solutions":["Add sync_dist=True to the epoch-level self.log call: self.log('val_loss', loss, on_epoch=True, sync_dist=True).","If you intentionally want rank-local values (rare), silence it explicitly by constructing the warning-free path or filtering PossibleUserWarning, and document why synchronization is unwanted.","Verify your metric semantics: for torchmetrics Metric objects, prefer passing the metric itself and let Lightning aggregate, or set sync_dist with an appropriate sync_dist_op/reduce_fx.","Confirm you actually run distributed: if torch.distributed is not initialized the warning is a false positive caused by a stale environment/world size; check trainer.world_size."],"exampleFix":"// before\nself.log(\"val_loss\", loss, on_step=False, on_epoch=True)\n\n// after\nself.log(\"val_loss\", loss, on_step=False, on_epoch=True, sync_dist=True)","handlingStrategy":"validation","validationCode":"from lightning.pytorch.utilities import rank_zero_only\n\ndef log_safe(module, name, value, on_epoch=True):\n    sync = module.trainer is not None and module.trainer.world_size > 1\n    module.log(name, value, on_epoch=on_epoch, sync_dist=sync)","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Default to sync_dist=True for every epoch-level metric; it is cheap relative to correct reporting.","Centralize logging in helper methods so the sync_dist policy is applied uniformly across the codebase.","In multi-device tests, assert that logged epoch metrics match single-device runs to catch missing sync_dist early."],"tags":["pytorch-lightning","distributed","ddp","logging","sync-dist","metrics"],"backgroundTag":"distributed-metric-sync-warning","analyzedSha":"9fed5c27d2a62ff0efd6c3573599921d6ff67c14","analyzedAt":"2026-08-28T11:52:41.083Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}