{"record":{"id":"37a231d6178f7985","repo":"nautechsystems/nautilus_trader","slug":"chart-renderer-must-be-callable-was-type-rendere","errorCode":null,"errorMessage":"Chart renderer must be callable, was {type(renderer)}","messagePattern":"Chart renderer must be callable, was (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"python/nautilus_trader/analysis/tearsheet.py","lineNumber":2468,"sourceCode":"        Display title for the subplot.\n    renderer : Callable\n        Function that adds traces to the figure. Signature:\n        renderer(fig, row, col, returns, stats_pnls, stats_returns, stats_general,\n        theme_config, benchmark_returns, benchmark_name, run_info, account_info, engine, **kwargs)\n\n    Raises\n    ------\n    ValueError\n        If name is empty or renderer is not callable.\n\n    \"\"\"\n    _require_not_none(name, \"name\")\n\n    if not name.strip():\n        raise ValueError(\"Chart name cannot be empty\")\n\n    if not callable(renderer):\n        raise ValueError(f\"Chart renderer must be callable, was {type(renderer)}\")\n\n    _TEARSHEET_CHART_SPECS[name] = {\n        \"type\": subplot_type,\n        \"title\": title,\n        \"renderer\": renderer,\n    }\n\n\ndef _calculate_grid_layout(\n    charts: list[TearsheetChart],\n    custom_layout: Any = None,\n) -> tuple[int, int, list, list[str], list[float], float, float]:\n    \"\"\"\n    Calculate dynamic grid layout based on selected charts.\n\n    Parameters\n    ----------\n    charts : list[TearsheetChart]","sourceCodeStart":2450,"sourceCodeEnd":2486,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/a4b06ed870971b5671d12754ea138a3ab99b1dec/python/nautilus_trader/analysis/tearsheet.py#L2450-L2486","documentation":"register_tearsheet_chart validates that the renderer argument is callable; anything else (a called result, a None default, an object) is rejected with the offending type in the message. The renderer is later invoked as renderer(fig=..., row=..., col=..., **kwargs) during figure assembly, so a non-callable would fail much later and less clearly.","triggerScenarios":"Passing renderer=render_chart() (calling the function, yielding its return value) instead of renderer=render_chart; passing a class instance without __call__; passing None or a string.","commonSituations":"Decorators/call-results mistaken for functions; wrapper objects that forgot __call__; copy-paste from an example where parentheses were added.","solutions":["Pass the function object itself: register_tearsheet_chart('x', 'xy', 'T', render_chart) with no parentheses","If wrapping an instance, give the class a __call__ method or pass a lambda closing over it","Confirm with callable(renderer) before registering"],"exampleFix":"# before\nregister_tearsheet_chart(name='c', subplot_type='xy', title='T', renderer=render_chart())\n# ValueError: Chart renderer must be callable, was Figure\n\n# after\nregister_tearsheet_chart(name='c', subplot_type='xy', title='T', renderer=render_chart)","handlingStrategy":"type-guard","validationCode":"if not callable(renderer):\n    raise TypeError(f'renderer must be callable, got {type(renderer).__name__}')\nregister_tearsheet_chart(name, 'xy', title, renderer)","typeGuard":"from typing import Callable\n\ndef is_renderer(obj: object) -> bool:\n    return callable(obj)","tryCatchPattern":"try:\n    register_tearsheet_chart(name, 'xy', title, renderer)\nexcept ValueError as e:\n    raise TypeError('Pass the function itself, not its result') from e","preventionTips":["Register functions by reference (no parentheses) or via the decorator form","Lint for common 'fn()' where 'fn' is expected in registration helpers"],"tags":["python","tearsheet","type-validation","callable","register-chart"],"backgroundTag":"type-validation-failed","analyzedSha":"a4b06ed870971b5671d12754ea138a3ab99b1dec","analyzedAt":"2026-08-16T22:54:50.089Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}