Running into a weird bug yesterday where a UI element was responding to a signal twice on every emission. Spent way too long chasing it before realizing I had the same signal connected in both the editor and in _ready(). Classic.
The problem is there's no quick way to audit what's actually wired up at runtime. Object.get_signal_connection_list() exists, but you have to query each signal by name — you can't just ask a node "what are all your active connections." So I wrote a small static utility that walks a node (optionally recursive) and prints everything:
class_name SignalDebug
static func debug_signals(node: Node, recursive: bool = false, depth: int = 0) -> void:
var prefix := " ".repeat(depth)
var printed_node := false
for sig in node.get_signal_list():
var connections := node.get_signal_connection_list(sig.name)
if connections.is_empty():
continue
if not printed_node:
print("%s[%s] %s" % [prefix, node.get_class(), node.name])
printed_node = true
for conn in connections:
var target := conn.callable.get_object()
var method := conn.callable.get_method()
var target_label := target.name if target is Node else target.get_class()
print("%s .%s → %s::%s" % [prefix, sig.name, target_label, method])
if recursive:
for child in node.get_children():
debug_signals(child, true, depth + 1)
Drop it in a file with class_name SignalDebug and call it anywhere:
SignalDebug.debug_signals(get_tree().root, true)
Output ends up looking like:
[CanvasLayer] HUD
.visibility_changed → GameManager::_on_hud_visibility_changed
[Button] StartButton
.pressed → UIController::_on_start_pressed
.pressed → AudioManager::_on_button_press ← this one definitely shouldn't still be here
Immediately obvious when you've got duplicate connections or leftover references from old refactors. First time I ran it on my current project I found four stale connections I didn't know existed, including one pointing to a freed node that somehow hadn't triggered an error yet.
It's become a staple in my debug builds. Curious if anyone's taken this further — an actual in-editor panel, maybe filtering to only show connections to external nodes, or hooking into the remote debugger protocol? I keep meaning to turn it into a proper plugin but never get around to it.