Quick answer: The most common cause is that the navigation mesh has not been baked, or the baked mesh does not cover the area between the agent and the target. NavigationAgent requires a NavigationRegion with a valid, baked navigation mesh. Without it, there is no navigable surface and no path can be calculated.
Here is how to fix Godot navigation agent path not found. Your enemy is supposed to chase the player, but it just stands there. The NavigationAgent2D has a target position, you are calling get_next_path_position() every frame, and the agent reports that it has already reached its target — even though it has not moved. Navigation pathfinding in Godot 4 has several configuration requirements that are easy to miss, and any one of them can silently prevent paths from being found.
The Symptom
Your NavigationAgent2D or NavigationAgent3D fails to produce a valid path. When you set target_position and check is_navigation_finished(), it returns true immediately, or get_next_path_position() returns the agent’s current position rather than a point along a path to the target. The agent does not move because there is no path to follow.
In some cases, the agent finds a partial path that stops well short of the target, or it finds a path that takes a wildly indirect route. These variations point to navigation mesh gaps or incorrect region connections rather than a complete failure to pathfind.
A third variant is the agent finding a path initially but then losing it after the target moves to a new location. If calling set_target_position() with updated coordinates does not produce a new path, the navigation server may not be updating, or the target has moved to a location outside the navigation mesh.
Debug visualization can help enormously here. Enable Debug > Visible Navigation in the editor toolbar when running the game. This draws the navigation mesh and active paths in the viewport, making it immediately obvious whether a valid mesh exists and where gaps are.
Navigation Mesh Basics
The navigation system in Godot requires three components working together: a NavigationRegion that defines the navigable area, a NavigationAgent that requests and follows paths, and the NavigationServer that computes paths between them. If any component is missing or misconfigured, pathfinding fails silently.
The NavigationRegion (NavigationRegion2D or NavigationRegion3D) holds a navigation mesh resource that describes where agents can walk. In 2D, this is a NavigationPolygon — a set of polygons that define walkable areas. In 3D, this is a NavigationMesh — a simplified mesh generated from your level geometry.
Critically, the navigation mesh must be baked before it works. An unbaked navigation mesh is empty, and the navigation server has no data to compute paths with. Baking analyzes your scene’s geometry and generates the navigable surface. Without this step, every path query returns an empty result.
To bake in the editor, select the NavigationRegion node and click the “Bake NavigationMesh” button in the toolbar. In 2D, you can also draw the NavigationPolygon manually using the polygon editing tools, which does not require baking.
Setting Up NavigationRegion and Baking
Here is the correct setup for a 2D navigation system:
# Scene tree structure:
# - Level (Node2D)
# - NavigationRegion2D
# - TileMapLayer (or other geometry)
# - Enemy (CharacterBody2D)
# - NavigationAgent2D
# - CollisionShape2D
# - Player (CharacterBody2D)
The NavigationRegion2D needs a NavigationPolygon resource assigned. For 2D tile-based games, you can configure the NavigationPolygon to parse geometry from child nodes. Set the Parsed Geometry Type to “Static Bodies” or “Mesh Instances” depending on what your level uses, then bake.
For 3D, the NavigationMesh resource has more configuration options. The most important are Agent Radius (how far from walls the mesh is shrunk) and Cell Size (the resolution of the voxelization). Smaller cell sizes produce more accurate meshes but take longer to bake and use more memory.
# Verify navigation mesh is valid at runtime
extends NavigationRegion2D
func _ready():
var nav_poly = navigation_polygon
if nav_poly == null:
push_error("No NavigationPolygon assigned!")
return
if nav_poly.get_polygon_count() == 0:
push_error("NavigationPolygon has no polygons - did you bake?")
return
print("Navigation mesh has ", nav_poly.get_polygon_count(), " polygons")
If the polygon count is zero, the mesh has not been baked or the bake produced no geometry. This happens when the NavigationRegion has no child geometry to parse, or when the parsing settings do not match the type of geometry in the scene.
Agent Configuration and Path Following
The NavigationAgent node is responsible for requesting paths from the server and providing waypoints for your movement code. A common mistake is not using it correctly in the movement loop:
extends CharacterBody2D
@export var speed: float = 200.0
@onready var nav_agent: NavigationAgent2D = $NavigationAgent2D
func _ready():
# Wait for the navigation server to sync
await get_tree().physics_frame
nav_agent.target_position = get_target_position()
func _physics_process(delta):
if nav_agent.is_navigation_finished():
return
var next_pos = nav_agent.get_next_path_position()
var direction = global_position.direction_to(next_pos)
velocity = direction * speed
move_and_slide()
func get_target_position() -> Vector2:
# Return the player's position or wherever you want to navigate to
return get_tree().get_first_node_in_group("player").global_position
The critical line in this code is the await get_tree().physics_frame in _ready(). The navigation server needs at least one physics frame to synchronize after the scene loads. If you set the target position immediately in _ready() without waiting, the server may not have registered the navigation regions yet, resulting in no path being found. This single-frame delay is the fix for a huge number of “navigation does not work on scene load” reports.
The agent_radius property on the NavigationAgent is also important. It determines how close to walls the agent will path. If this radius is larger than the margin that was used when baking the navigation mesh, the agent may find that corridors are too narrow to traverse, even though the mesh shows them as navigable. Set the agent radius to match or be slightly smaller than the bake radius.
The path_desired_distance and target_desired_distance properties control how close the agent needs to get to waypoints and the final target before considering them reached. If these are too large, the agent may skip waypoints or declare the path finished prematurely. If they are too small, the agent may oscillate around a waypoint without ever reaching it.
Region Connections and Navigation Layers
If your level uses multiple NavigationRegion nodes (for example, separate rooms or floors), the regions must either overlap or be close enough for the server to connect them. The edge connection margin determines how close two region edges must be to form a connection.
# Configure navigation map edge connection margin
func _ready():
var map_rid = get_world_2d().navigation_map
NavigationServer2D.map_set_edge_connection_margin(map_rid, 10.0)
print("Edge connection margin: ",
NavigationServer2D.map_get_edge_connection_margin(map_rid))
If two regions are too far apart, the server cannot connect their edges, and paths that would need to cross between regions will fail. Increase the edge connection margin or ensure your regions overlap slightly at their borders.
Navigation layers add another dimension to this. Both NavigationRegion and NavigationAgent have navigation_layers properties (bitmasks). The agent will only path through regions whose navigation layers overlap with the agent’s navigation layers. If an agent is on layer 1 and a region is on layer 2, that region is invisible to the agent. This is useful for different-sized enemies (small enemies can go through narrow passages that large enemies cannot), but it can cause confusion when layers do not match.
To debug layer issues, print the navigation layers of both the agent and all regions in the scene:
# Diagnostic: check navigation layer alignment
func _ready():
var agent = $NavigationAgent2D
print("Agent nav layers: ", agent.navigation_layers)
for region in get_tree().get_nodes_in_group("nav_regions"):
print("Region ", region.name, " nav layers: ", region.navigation_layers)
Runtime Baking and Dynamic Obstacles
If your level changes at runtime (doors opening, bridges being built, terrain deforming), you need to rebake the navigation mesh or use navigation obstacles to update the pathfinding data.
For dynamic obstacles like doors or moving platforms, use NavigationObstacle2D/3D nodes. These carve holes in the navigation mesh at runtime without requiring a full rebake. The obstacle’s shape defines the area that becomes non-navigable, and paths will automatically route around it.
# Toggle a door obstacle
func open_door():
$NavigationObstacle2D.avoidance_enabled = false
# Or remove the obstacle entirely
$NavigationObstacle2D.queue_free()
func close_door():
$NavigationObstacle2D.avoidance_enabled = true
For larger changes that obstacles cannot handle, you can rebake the navigation mesh at runtime. This is more expensive but produces accurate results for arbitrary geometry changes:
# Runtime rebake (can be slow for large meshes)
func rebuild_navigation():
var region = $NavigationRegion2D
region.bake_navigation_polygon()
# Wait for bake to complete before querying paths
await region.bake_finished
print("Navigation mesh rebaked")
Be aware that rebaking is not instant, especially for large or complex meshes. During the bake, existing paths remain valid but new path queries may return stale results. Plan your rebakes for moments when brief navigation inaccuracy is acceptable, like during a screen transition or when the player is far from AI agents.
"The await physics_frame trick in _ready is the single most important thing to know about Godot navigation. Without it, every path query on the first frame returns nothing, and people think the entire system is broken."
Related Issues
If your agents find paths but collide with each other or overlap, see our guide on Area2D signal detection for proximity checks. For enemies that path correctly but jitter when reaching their target, check CharacterBody2D jittering against surfaces. If your navigation works in the editor but fails in exported builds, missing resources in exports may explain why the navigation mesh data is not being included.
Await one physics frame in _ready before setting the target. Always.