Skip to content

serialize() crashes with TypeError when panel data contains dicts with non-string keys #2317

@bleadof

Description

@bleadof

Environment

  • Django Debug Toolbar: 6.2.0 (also reproduced on 6.1.0)
  • Python: 3.12
  • Django: 5.2

Error

File "debug_toolbar/store.py", in save_panel
    cls._request_store[request_id][panel_id] = serialize(data)
File "debug_toolbar/store.py", in serialize
    return json.dumps(data, cls=DebugToolbarJSONEncoder)
TypeError: keys must be str, int, float, bool or None, not tuple

Root cause?

DebugToolbarJSONEncoder.default() seems to handle non-serializable values via force_str, but Python's JSON C encoder raises TypeError for non-string dict keys before default() is ever invoked. I guess it's a known issue since store.py already has a comment describing this failure?

Affected panels

CachePanel - triggered when a cached value is a dict with non-string keys:

from uuid import UUID
from django.core.cache import cache

cache.set("my_key", {
    (UUID("aaaaaaaa-0000-0000-0000-000000000001"), UUID("bbbbbbbb-0000-0000-0000-000000000002")): {"value": 1},
    (UUID("dddddddd-0000-0000-0000-000000000004"), UUID("eeeeeeee-0000-0000-0000-000000000005")): {"value": 2},
}, timeout=300)

_store_call_info captures the raw args tuple (including the dict value) into self.calls. When generate_stats serializes self.calls, json.dumps raises on the tuple keys inside that dict.

RequestPanel - triggered when a session value is a dict with non-string keys:

request.session["lookup"] = {(1, 2): "foo", (3, 4): "bar"}

sanitize_and_sort_request_vars wraps the session in {"list": [...]} but passes dict values through unchanged, so the nested dict with tuple keys reaches serialize() and crashes.

Workaround

Exclude the affected panels in DEBUG_TOOLBAR_PANELS in Django settings.py:

DEBUG_TOOLBAR_PANELS = [
    "debug_toolbar.panels.history.HistoryPanel",
    "debug_toolbar.panels.versions.VersionsPanel",
    "debug_toolbar.panels.timer.TimerPanel",
    "debug_toolbar.panels.settings.SettingsPanel",
    "debug_toolbar.panels.headers.HeadersPanel",
    # "debug_toolbar.panels.request.RequestPanel",
    "debug_toolbar.panels.sql.SQLPanel",
    "debug_toolbar.panels.staticfiles.StaticFilesPanel",
    "debug_toolbar.panels.templates.TemplatesPanel",
    "debug_toolbar.panels.alerts.AlertsPanel",
    # "debug_toolbar.panels.cache.CachePanel",
    "debug_toolbar.panels.signals.SignalsPanel",
    "debug_toolbar.panels.logging.LoggingPanel",
    "debug_toolbar.panels.redirects.RedirectsPanel",
    "debug_toolbar.panels.profiling.ProfilingPanel",
]

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions