Lightning-AI/pytorch-lightning · error · ValueError

`self.log_dict({dictionary})` was called, but nested diction

Error message

`self.log_dict({dictionary})` was called, but nested dictionaries cannot be logged

What it means

In Fabric mode (LightningModule used via lightning.fabric), _log_dict_through_fabric validates that the dictionary passed to self.log_dict contains only flat values. Any value that is itself a dict makes serialization ambiguous and raises ValueError.

Source

Thrown at src/lightning/pytorch/core/module.py:642

                enable_graph=enable_graph,
                sync_dist=sync_dist,
                sync_dist_group=sync_dist_group,
                add_dataloader_idx=add_dataloader_idx,
                batch_size=batch_size,
                rank_zero_only=rank_zero_only,
            )
        return None

    def _log_dict_through_fabric(
        self, dictionary: Union[Mapping[str, _METRIC], MetricCollection], logger: Optional[bool] = None
    ) -> None:
        if logger is False:
            # Passing `logger=False` with Fabric does not make much sense because there is no other destination to
            # log to, but we support it in case the original code was written for Trainer use
            return

        if any(isinstance(v, dict) for v in dictionary.values()):
            raise ValueError(f"`self.log_dict({dictionary})` was called, but nested dictionaries cannot be logged")
        for name, value in dictionary.items():
            apply_to_collection(value, object, self.__check_allowed, name, value, wrong_dtype=(numbers.Number, Tensor))

        assert self._fabric is not None
        self._fabric.log_dict(metrics=dictionary)  # type: ignore[arg-type]

    @staticmethod
    def __check_not_nested(value: dict, name: str) -> None:
        # self-imposed restriction. for simplicity
        if any(isinstance(v, dict) for v in value.values()):
            raise ValueError(f"`self.log({name}, {value})` was called, but nested dictionaries cannot be logged")

    @staticmethod
    def __check_allowed(v: Any, name: str, value: Any) -> None:
        raise ValueError(f"`self.log({name}, {value})` was called, but `{type(v).__name__}` values cannot be logged")

    def __to_tensor(self, value: Union[Tensor, numbers.Number], name: str) -> Tensor:
        value = (

View on GitHub (pinned to 9fed5c27d2)

Solutions

  1. Flatten the dictionary into scalar/tensor leaves: {'outer/inner': 1}
  2. Log each inner key separately with self.log_dict on the flattened mapping

Example fix

# before
self.log_dict({'train': {'loss': loss, 'acc': acc}})

# after
self.log_dict({'train/loss': loss, 'train/acc': acc})
Defensive patterns

Strategy: validation

Validate before calling

def flatten(d, prefix=''):
    out = {}
    for k, v in d.items():
        key = f'{prefix}/{k}' if prefix else k
        if isinstance(v, dict):
            out.update(flatten(v, key))
        else:
            out[key] = v
    return out
self.log_dict(flatten(metrics))

Type guard

def is_flat_metric_dict(d) -> bool:
    return all(not isinstance(v, dict) for v in d.values())

Prevention

When it happens

Trigger: Calling self.log_dict({'outer': {'inner': 1}}) on a module attached to Fabric (self._fabric is not None).

Common situations: User migrated Trainer code to Fabric and logged a nested metrics structure; aggregated metrics into per-dataset dicts before logging.

Related errors


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