{"record":{"id":"eb22c29e83daa43f","repo":"Lightning-AI/pytorch-lightning","slug":"self-log-name-value-was-called-but-nested","errorCode":null,"errorMessage":"`self.log({name}, {value})` was called, but nested dictionaries cannot be logged","messagePattern":"`self\\.log\\((.+?), (.+?)\\)` was called, but nested dictionaries cannot be logged","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/lightning/pytorch/core/module.py","lineNumber":653,"sourceCode":"    ) -> None:\n        if logger is False:\n            # Passing `logger=False` with Fabric does not make much sense because there is no other destination to\n            # log to, but we support it in case the original code was written for Trainer use\n            return\n\n        if any(isinstance(v, dict) for v in dictionary.values()):\n            raise ValueError(f\"`self.log_dict({dictionary})` was called, but nested dictionaries cannot be logged\")\n        for name, value in dictionary.items():\n            apply_to_collection(value, object, self.__check_allowed, name, value, wrong_dtype=(numbers.Number, Tensor))\n\n        assert self._fabric is not None\n        self._fabric.log_dict(metrics=dictionary)  # type: ignore[arg-type]\n\n    @staticmethod\n    def __check_not_nested(value: dict, name: str) -> None:\n        # self-imposed restriction. for simplicity\n        if any(isinstance(v, dict) for v in value.values()):\n            raise ValueError(f\"`self.log({name}, {value})` was called, but nested dictionaries cannot be logged\")\n\n    @staticmethod\n    def __check_allowed(v: Any, name: str, value: Any) -> None:\n        raise ValueError(f\"`self.log({name}, {value})` was called, but `{type(v).__name__}` values cannot be logged\")\n\n    def __to_tensor(self, value: Union[Tensor, numbers.Number], name: str) -> Tensor:\n        value = (\n            value.clone().detach()\n            if isinstance(value, Tensor)\n            else torch.tensor(value, device=self.device, dtype=_get_default_dtype())\n        )\n        if not torch.numel(value) == 1:\n            raise ValueError(\n                f\"`self.log({name}, {value})` was called, but the tensor must have a single element.\"\n                f\" You can try doing `self.log({name}, {value}.mean())`\"\n            )\n        value = value.squeeze()\n        return value","sourceCodeStart":635,"sourceCodeEnd":671,"githubUrl":"https://github.com/Lightning-AI/pytorch-lightning/blob/9fed5c27d2a62ff0efd6c3573599921d6ff67c14/src/lightning/pytorch/core/module.py#L635-L671","documentation":"__check_not_nested rejects dictionaries passed to self.log whose values contain other dicts. This is a self-imposed simplicity restriction in the Fabric logging path: logged dicts must be flat maps of scalar/tensor values.","triggerScenarios":"self.log(name, {'a': {'b': 1}}) or any dict value containing a dict when running under Fabric logging.","commonSituations":"Logging grouped/nested experiment configs or hierarchical metric trees directly instead of flattening keys.","solutions":["Flatten nested keys with a separator before logging","Use self.log_dict on the flattened dict"],"exampleFix":"# before\nself.log('metrics', {'loss': {'total': l}})\n\n# after\nself.log('metrics/loss/total', l)","handlingStrategy":"type-guard","validationCode":"if any(isinstance(v, dict) for v in value.values()):\n    value = flatten(value)  # reuse a flatten helper\nself.log(name, value)","typeGuard":"def is_not_nested(d) -> bool:\n    return not any(isinstance(v, dict) for v in d.values())","tryCatchPattern":null,"preventionTips":["Never log dicts-of-dicts; flatten before logging","Add a unit test asserting logged payloads are flat"],"tags":["pytorch-lightning","fabric","self-log","nested-dict","validation"],"backgroundTag":"nested-metrics-dict","analyzedSha":"9fed5c27d2a62ff0efd6c3573599921d6ff67c14","analyzedAt":"2026-08-28T11:52:41.083Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}