Quick answer: NavigationObstacle nodes affect avoidance behavior, not the navigation mesh itself. If your navigation mesh is baked over the obstacle area, the pathfinding algorithm will route through it.

Here is how to fix Godot pathfinding ignoring obstacles. You have placed NavigationObstacle2D or NavigationObstacle3D nodes in your scene, but your agents walk straight through them as if they do not exist. Alternatively, you have set up navigation links or modifiers but the pathfinding algorithm completely ignores them. This is a layer configuration issue, and it is one of the most frequently misunderstood parts of Godot 4’s navigation system.

The Symptom

You add a NavigationObstacle2D to your scene—maybe a barrel, a fence, or a dynamic enemy—and expect your NavigationAgent to route around it. But the agent plots a path straight through the obstacle. You see one or more of these behaviors:

The frustrating part is that everything looks correct in the inspector. The nodes are there, the properties are set, but the system behaves as if the obstacles are invisible.

What Causes This

There are three distinct causes, depending on which part of the navigation system you are working with:

Cause 1: Avoidance layers are mismatched. Godot 4’s navigation uses a layer/mask system similar to collision layers. Each NavigationObstacle has avoidance_layers (which layers it occupies), and each NavigationAgent has avoidance_mask (which layers it responds to). If there is no overlap between the obstacle’s layers and the agent’s mask, the agent will completely ignore the obstacle. By default, both are set to layer 1, but if you have changed either one, they may no longer match.

Cause 2: The obstacle radius is zero or too small. NavigationObstacle nodes have a radius property that defines the avoidance area. If this is left at 0 (the default for newly created nodes in some Godot versions), the obstacle has no avoidance footprint and agents have nothing to avoid.

Cause 3: Confusing avoidance with pathfinding. This is the most common conceptual mistake. NavigationObstacle nodes affect the real-time avoidance system, not the pathfinding algorithm. The pathfinding algorithm only considers the navigation mesh. If your navigation mesh is baked over the area where the obstacle sits, the pathfinder will happily route through it. The avoidance system may push the agent aside at the last moment, but the path itself will still go through the obstacle area.

The Fix

The fix depends on whether you need dynamic avoidance or static pathfinding exclusion. Most projects need both.

Step 1: Fix avoidance layer configuration.

extends CharacterBody2D

@onready var nav_agent: NavigationAgent2D = $NavigationAgent2D

func _ready():
  # Enable avoidance on the agent
  nav_agent.avoidance_enabled = true
  nav_agent.radius = 16.0

  # Set which avoidance layers this agent avoids
  # Bit 1 = general obstacles, Bit 2 = enemies
  nav_agent.avoidance_mask = 1 | 2  # avoid layers 1 and 2
  nav_agent.avoidance_layers = 1     # this agent is on layer 1

  # Connect the velocity_computed signal
  nav_agent.velocity_computed.connect(_on_velocity_computed)

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)
  nav_agent.velocity = direction * 200.0

func _on_velocity_computed(safe_velocity: Vector2):
  velocity = safe_velocity
  move_and_slide()

Step 2: Configure the obstacle correctly.

# On the obstacle node (e.g., a barrel or crate)
extends Node2D

@onready var obstacle: NavigationObstacle2D = $NavigationObstacle2D

func _ready():
  # Set a meaningful radius (in pixels for 2D)
  obstacle.radius = 32.0

  # Set which avoidance layer this obstacle occupies
  obstacle.avoidance_layers = 1

  # Enable the obstacle to affect avoidance
  obstacle.avoidance_enabled = true

Step 3: For static obstacles, exclude them from the navigation mesh. If the obstacle is static and should never be walked through, the correct approach is to ensure the navigation mesh does not cover that area. Add the obstacle’s collision shape to the geometry that the NavigationRegion2D uses for baking:

# In the NavigationRegion2D setup
# 1. Set the NavigationPolygon source geometry mode
#    to "Root Node Children"
# 2. Add your obstacle's StaticBody2D as a child
#    of the NavigationRegion2D (or in parsed groups)
# 3. Set the obstacle's collision layer to match
#    the parsed collision mask in NavigationPolygon
# 4. Re-bake the navigation mesh

# To bake at runtime after adding obstacles:
@onready var nav_region: NavigationRegion2D = $NavigationRegion2D

func rebake_navigation():
  nav_region.bake_navigation_polygon()

Step 4: For navigation links, verify the connection. Navigation links connect two disconnected navigation mesh regions. Both ends of the link must be within a valid navigation mesh polygon, and the link must be enabled:

# Verify a NavigationLink2D is working
@onready var nav_link: NavigationLink2D = $NavigationLink2D

func _ready():
  nav_link.enabled = true
  nav_link.bidirectional = true

  # Both start and end must be on valid nav mesh
  var map_rid = nav_link.get_navigation_map()
  var start_on_mesh = NavigationServer2D.map_get_closest_point(
    map_rid, nav_link.start_position
  )
  var end_on_mesh = NavigationServer2D.map_get_closest_point(
    map_rid, nav_link.end_position
  )
  print("Link start maps to: ", start_on_mesh)
  print("Link end maps to: ", end_on_mesh)

Why This Works

Godot 4’s navigation system has two separate subsystems that are often confused:

For a complete solution, static obstacles should be excluded from the navigation mesh (so pathfinding routes around them) while dynamic obstacles should use the avoidance system (so agents steer around them in real time). The layer/mask configuration ensures that agents only respond to relevant obstacles, which is important for performance when you have many agents and obstacles in a scene.

Think of it this way: pathfinding is the GPS that plans the route. Avoidance is the driver who swerves to avoid a pedestrian. You need both systems configured correctly for reliable navigation.

Related Issues

If your agent is reaching the target area but overshooting or jittering at the destination, that is a distance threshold issue rather than an obstacle problem. See Fix: Godot NavigationAgent Overshooting Target Point for the solution.

For tile-based games where navigation interacts with TileMap layers, rendering order issues can make it hard to tell where the navigation mesh actually is. Check Fix: Godot TileMap Layers Drawing in Wrong Order if your debug visualization seems off.

Avoidance is not pathfinding. Configure both.