Lightning-AI/pytorch-lightning · error · ValueError

Device should be CPU, got {device} instead.

Error message

Device should be CPU, got {device} instead.

What it means

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.

Source

Thrown at src/lightning/fabric/accelerators/cpu.py:34

import torch
from typing_extensions import override

from lightning.fabric.accelerators.accelerator import Accelerator
from lightning.fabric.accelerators.registry import _AcceleratorRegistry


class CPUAccelerator(Accelerator):
    """Accelerator for CPU devices."""

    @override
    def setup_device(self, device: torch.device) -> None:
        """
        Raises:
            ValueError:
                If the selected device is not CPU.
        """
        if device.type != "cpu":
            raise ValueError(f"Device should be CPU, got {device} instead.")

    @override
    def teardown(self) -> None:
        pass

    @staticmethod
    @override
    def parse_devices(devices: Union[int, str]) -> int:
        """Accelerator device parsing logic."""
        return _parse_cpu_cores(devices)

    @staticmethod
    @override
    def get_parallel_devices(devices: Union[int, str]) -> list[torch.device]:
        """Gets parallel devices for the Accelerator."""
        devices = _parse_cpu_cores(devices)
        return [torch.device("cpu")] * devices

View on GitHub (pinned to 9fed5c27d2)

Solutions

  1. Make all self.log calls for the same metric name use identical arguments (on_step, on_epoch, sync_dist, reduce_fx, prog_bar, etc.)
  2. If you need a different aggregation, log under a different name (e.g. 'loss_step' and 'loss_epoch')
  3. Audit callbacks/on_train_batch_end for duplicate self.log calls on the same key as training_step
  4. Move the second log call to a different hook so it lands in a different fx bucket

Example fix

# before
self.log("loss", loss, on_step=True)
self.log("loss", loss, on_epoch=True)  # raises

# after
self.log("loss", loss, on_step=True, on_epoch=True)
# or use distinct names
self.log("loss_step", loss, on_step=True)
self.log("loss_epoch", loss, on_epoch=True)
Defensive patterns

Strategy: validation

Validate before calling

_LOGGED = {}

def log_once(name, value, **kwargs):
    key = (fx_name(), name)  # e.g. current hook
    meta = tuple(sorted(kwargs.items()))
    if key in _LOGGED and _LOGGED[key] != meta:
        raise RuntimeError(f"self.log({name}) called twice in {key[0]} with different args")
    _LOGGED.setdefault(key, meta)
    self.log(name, value, **kwargs)

Type guard

def consistent_log_meta(name: str, kwargs: dict, logged: dict[tuple[str, str], tuple]) -> bool:
    key = (current_fx(), name)
    meta = tuple(sorted(kwargs.items()))
    return logged.get(key, meta) == meta

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of Lightning-AI/pytorch-lightning@9fed5c27d2 (2026-08-28). Data as JSON: /api/errors/132eb8f8f3654ae0. Report an issue: GitHub.