name: debug-issue description: Systematic Godot debugging decision trees for physics, signals, rendering, navigation, and input issues argument-hint: "[symptom description]"
Debug Issue
Systematic debugging using domain-specific decision trees. Start by identifying the symptom category, then follow the corresponding tree.
Category Detection
Map user's symptom to the right tree:
| Symptom keywords | Category |
|---|---|
| "pass through", "no collision", "overlap", "stuck", "slides off" | Physics |
| "signal not firing", "not connected", "callback not called" | Signals |
| "invisible", "not showing", "behind", "flickering", "wrong color" | Rendering |
| "not moving to target", "path wrong", "stuck on nav", "no path" | Navigation |
| "key not working", "input ignored", "wrong button", "double input" | Input |
Physics Decision Tree
Check in this order -- each step is the most common cause at that point:
Collision layer/mask mismatch (most common):
- Check defined layer names:
physics(action="layers") - Check node properties:
nodes(action="get_property", scene_path="<scene>.tscn", name="<body>", property="collision_layer") nodes(action="get_property", scene_path="<scene>.tscn", name="<body>", property="collision_mask") - Rule: Object A detects Object B only if A's
collision_maskhas a bit that matches B'scollision_layer. Both must be set correctly.
- Check defined layer names:
Missing CollisionShape2D/3D:
- Inspect the scene tree:
scenes(action="info", scene_path="<scene>.tscn") - Verify every physics body and Area has a CollisionShape child. A body without a shape = invisible to physics.
- Inspect the scene tree:
Wrong body type:
StaticBody2D: immovable (walls, floors)CharacterBody2D: player/NPC movement viamove_and_slide()RigidBody2D: physics-driven (projectiles, debris)- Common mistake: using
RigidBody2Dfor a player character, then fighting the physics engine
Script velocity issues:
- Read the script:
scripts(action="read", script_path="res://scripts/<name>.gd") - Check if
velocityis being set beforemove_and_slide() - Check if gravity is applied in
_physics_process(not_process) - Check if
deltais used for frame-independent movement
- Read the script:
Signals Decision Tree
Connection exists?
- List all connections:
signals(action="list", scene_path="<scene>.tscn") - Missing connection = signal silently does nothing
- List all connections:
Signature match?
- Signal definition parameters must match handler function parameters
signal health_changed(new_hp: int)requires handlerfunc _on_health_changed(new_hp: int)- Extra or missing parameters = Godot error at runtime
Signal emitted?
- Add
print("signal emitted")beforeemit_signal()/signal.emit() - If not printed, the emit call is never reached (logic bug)
- Add
Callable valid?
- If connected via code: target node must exist when
connect()runs - If target is freed, signal emits to invalid callable = error
- If connected via code: target node must exist when
Rendering Decision Tree
Visibility:
visibleproperty = false? Check node and ALL parents (parent invisible = children invisible)modulate.a= 0? (fully transparent)
Z-index:
- Lower z-index renders behind higher z-index
z_as_relative= true means z_index is relative to parent- Sprite behind a TileMapLayer? Check z_index values on both
Material/shader:
- CanvasItemMaterial or ShaderMaterial overriding appearance?
- Shader compilation errors make object invisible (check Output panel)
Viewport issues:
- SubViewport not updating? Check
render_target_update_mode - Camera2D not active? Only one Camera2D should have
enabled = trueper viewport
- SubViewport not updating? Check
Navigation Decision Tree
NavigationRegion setup:
NavigationRegion2D/3Dmust exist in scene- Must have a
NavigationPolygon/NavigationMeshresource assigned
Navigation mesh baked?
- Mesh must be baked (either in editor or via
bake_navigation_mesh()) - Unbaked mesh = no paths available
- Mesh must be baked (either in editor or via
NavigationAgent config:
path_desired_distance: how close before moving to next path pointtarget_desired_distance: how close to target before stopping- Values too small = agent oscillates; too large = imprecise
Path calculation:
- Call
NavigationAgent.set_target_position()then checkget_next_path_position() - If returns current position, no valid path exists (check mesh coverage)
- Call
Input Decision Tree
Input Map:
- List actions:
input_map(action="list") - Check
project.godotfor[input]section
- List actions:
Action name match:
Input.is_action_pressed("jump")-- exact string match required- Typo in action name = silently returns false (no error!)
Event processing:
_input()vs_unhandled_input()-- if another node consumes the event,_unhandled_inputnever firesset_process_input(false)disables_input()on that node
Conflicts:
- Two actions on same key = both fire
- UI elements (Button, LineEdit) consume input events before game nodes
Diagnostic Steps
For any category:
- Inspect the scene tree:
scenes(action="info", scene_path="<scene>.tscn") - Read relevant scripts:
scripts(action="read", script_path="res://scripts/<name>.gd") - Check properties:
nodes(action="get_property", scene_path="<scene>.tscn", name="<node_path>", property="<name>") - Run the scene:
project(action="run", scene_path="<scene>.tscn")and check output for errors - Report findings: present root cause and specific fix (property change, missing node, script edit)
When to Use
- Any "it doesn't work" debugging scenario in Godot
- Systematic elimination when the cause is not obvious
- Verifying correct setup of physics, signals, rendering, navigation, or input