{"record":{"id":"132eb8f8f3654ae0","repo":"Lightning-AI/pytorch-lightning","slug":"device-should-be-cpu-got-device-instead","errorCode":null,"errorMessage":"Device should be CPU, got {device} instead.","messagePattern":"Device should be CPU, got (.+?) instead\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/lightning/fabric/accelerators/cpu.py","lineNumber":34,"sourceCode":"import torch\nfrom typing_extensions import override\n\nfrom lightning.fabric.accelerators.accelerator import Accelerator\nfrom lightning.fabric.accelerators.registry import _AcceleratorRegistry\n\n\nclass CPUAccelerator(Accelerator):\n    \"\"\"Accelerator for CPU devices.\"\"\"\n\n    @override\n    def setup_device(self, device: torch.device) -> None:\n        \"\"\"\n        Raises:\n            ValueError:\n                If the selected device is not CPU.\n        \"\"\"\n        if device.type != \"cpu\":\n            raise ValueError(f\"Device should be CPU, got {device} instead.\")\n\n    @override\n    def teardown(self) -> None:\n        pass\n\n    @staticmethod\n    @override\n    def parse_devices(devices: Union[int, str]) -> int:\n        \"\"\"Accelerator device parsing logic.\"\"\"\n        return _parse_cpu_cores(devices)\n\n    @staticmethod\n    @override\n    def get_parallel_devices(devices: Union[int, str]) -> list[torch.device]:\n        \"\"\"Gets parallel devices for the Accelerator.\"\"\"\n        devices = _parse_cpu_cores(devices)\n        return [torch.device(\"cpu\")] * devices\n","sourceCodeStart":16,"sourceCodeEnd":52,"githubUrl":"https://github.com/Lightning-AI/pytorch-lightning/blob/9fed5c27d2a62ff0efd6c3573599921d6ff67c14/src/lightning/fabric/accelerators/cpu.py#L16-L52","documentation":"Lightning's ResultCollection stores one _ResultMetric per logged key. When you call self.log(name, ...) multiple times with the same name in the same training/validation step, the metadata (on_step, on_epoch, sync_dist, prog_bar, etc.) of subsequent calls must exactly match the first call. The error is raised when the same key is re-logged with different logging arguments, since Lightning cannot reconcile two different aggregations for one key.","triggerScenarios":"Calling self.log('loss', ..., on_step=True) in training_step and then self.log('loss', ..., on_epoch=True) (different meta) within the same loop; logging the same metric name from training_step and a callback like on_train_batch_end attached to the same result object; changing sync_dist or prog_bar between two self.log calls for the same name and fx.","commonSituations":"Refactoring a LightningModule and adding a duplicate log call for the same key with different flags; logging in both the model and a callback; mixing manual optimization log calls with different reduce_fx; copy-pasting log lines and tweaking arguments.","solutions":["Make all self.log calls for the same metric name use identical arguments (on_step, on_epoch, sync_dist, reduce_fx, prog_bar, etc.)","If you need a different aggregation, log under a different name (e.g. 'loss_step' and 'loss_epoch')","Audit callbacks/on_train_batch_end for duplicate self.log calls on the same key as training_step","Move the second log call to a different hook so it lands in a different fx bucket"],"exampleFix":"# before\nself.log(\"loss\", loss, on_step=True)\nself.log(\"loss\", loss, on_epoch=True)  # raises\n\n# after\nself.log(\"loss\", loss, on_step=True, on_epoch=True)\n# or use distinct names\nself.log(\"loss_step\", loss, on_step=True)\nself.log(\"loss_epoch\", loss, on_epoch=True)","handlingStrategy":"validation","validationCode":"_LOGGED = {}\n\ndef log_once(name, value, **kwargs):\n    key = (fx_name(), name)  # e.g. current hook\n    meta = tuple(sorted(kwargs.items()))\n    if key in _LOGGED and _LOGGED[key] != meta:\n        raise RuntimeError(f\"self.log({name}) called twice in {key[0]} with different args\")\n    _LOGGED.setdefault(key, meta)\n    self.log(name, value, **kwargs)","typeGuard":"def consistent_log_meta(name: str, kwargs: dict, logged: dict[tuple[str, str], tuple]) -> bool:\n    key = (current_fx(), name)\n    meta = tuple(sorted(kwargs.items()))\n    return logged.get(key, meta) == meta","tryCatchPattern":null,"preventionTips":["Define one helper log() wrapper per LightningModule so every call for a metric uses the same flags","Search your module for duplicate self.log('<name>' calls and unify their keyword arguments","Remember callbacks and hooks share the same result collection per fx; use distinct metric names across them"],"tags":["pytorch-lightning","self-log","duplicate-metric","misconfiguration"],"backgroundTag":"duplicate-metric-logging","analyzedSha":"9fed5c27d2a62ff0efd6c3573599921d6ff67c14","analyzedAt":"2026-08-28T11:52:41.083Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}