Quick answer: Set the RayCast’s enabled = true, point target_position in the desired direction with non-zero length, and confirm the collision_mask includes the target’s layer. Call force_raycast_update() if you query immediately after moving the ray.

Here is how to fix Godot RayCast not detecting collision. You add a RayCast2D or RayCast3D to your player for ground detection, line-of-sight checks, or weapon aiming. You call is_colliding() and it always returns false, even when the ray visually passes through a collision shape. Godot raycasts have several independent settings that must all be correct for detection to work, and the defaults are not always what you expect.

The Symptom

is_colliding() returns false regardless of what is in the ray’s path. get_collider() returns null. The debug draw (visible in the editor viewport when the ray is selected) may show the ray pointing in the expected direction, but no collision point is reported at runtime.

Variant: the ray detects its own parent body instead of the intended target. Or the ray works for one frame after scene load then stops. Or it works in the editor preview but not in the running game.

What Causes This

RayCast not enabled. In Godot 4, both RayCast2D and RayCast3D default to enabled = false. A disabled raycast does not perform any physics queries and always returns false for is_colliding(). This is the single most common cause.

target_position is zero or wrong direction. The target_position property defines the ray’s endpoint relative to the node’s origin. The default is Vector2(0, 50) for 2D (pointing down) and Vector3(0, -1, 0) for 3D (pointing down). If you set it to Vector2.ZERO or a very small value, the ray has zero length and cannot intersect anything.

Collision mask mismatch. The raycast’s collision_mask must include the physics layer of the object you want to detect. If your ground is on Layer 2 and your ray’s mask only includes Layer 1, no ground collision is ever reported.

Querying before physics update. RayCasts update their collision state once per physics frame. If you move the ray and check is_colliding() in the same frame (or in _process between physics ticks), you get the previous frame’s result. This causes intermittent or one-frame-late detection.

Ray hitting its own parent. By default, exclude_parent is true, but if you reparent the ray or the collider hierarchy is unusual, the ray may hit the parent body’s collider before reaching the target. If exclude_parent was turned off, the parent consumes the ray.

The Fix

Step 1: Enable the raycast.

extends CharacterBody2D

func _ready():
    $RayCast2D.enabled = true

Or check the Enabled box in the Inspector. This single setting fixes the majority of “raycast not working” reports.

Step 2: Set target_position correctly. The target is relative to the RayCast node, not global. For a downward ground check on a 2D character:

# Points 40 pixels downward from the ray's origin
$RayCast2D.target_position = Vector2(0, 40)

For a 3D forward-facing ray (weapon aim):

# Points 100 units forward in the ray's local -Z direction
$RayCast3D.target_position = Vector3(0, 0, -100)

If you need the ray to follow the mouse or joystick direction, update target_position each frame in _physics_process:

func _physics_process(delta):
    var aim_dir = (get_global_mouse_position() - global_position).normalized()
    $RayCast2D.target_position = aim_dir * 300

    if $RayCast2D.is_colliding():
        var hit = $RayCast2D.get_collider()
        print("Hit: ", hit.name)

Step 3: Match collision masks. Select the RayCast in the Inspector. Under Collision, set the Mask bits to match the layers your targets are on. For example, if enemies are on Layer 3:

You can also set masks in code:

# Detect layers 2 and 3 only
$RayCast2D.collision_mask = 0b110  # bits 2 and 3

Step 4: Use force_raycast_update for immediate queries. If you reposition the ray and need the result in the same frame:

func check_line_of_sight(from: Vector2, to: Vector2):
    $RayCast2D.global_position = from
    $RayCast2D.target_position = to - from
    $RayCast2D.force_raycast_update()

    return not $RayCast2D.is_colliding()

force_raycast_update() runs a synchronous physics query, bypassing the wait-for-next-physics-frame delay. Use sparingly — calling it every frame on many rays is more expensive than letting normal updates happen.

Excluding Specific Colliders

To ignore certain objects, use the exclusion list:

# Ignore the player's own collider
$RayCast2D.add_exception(self)

# Ignore a specific node
$RayCast2D.add_exception($Shield)

# Clear all exceptions
$RayCast2D.clear_exceptions()

The exclude_parent property is a convenience shortcut for the most common case. When your raycast is a child of a CharacterBody2D, you almost always want this on so the ray does not immediately hit the parent’s collider.

Debugging Rays Visually

Enable Debug > Visible Collision Shapes in the editor to see rays at runtime. The ray draws as a line from origin to target_position. A red point appears at the collision point when a hit is detected. If you do not see the line at all, the ray is likely disabled or target_position is zero.

For 3D, you can also use DebugDraw or draw a line manually:

func _physics_process(delta):
    if $RayCast3D.is_colliding():
        var point = $RayCast3D.get_collision_point()
        DebugDraw3D.draw_line($RayCast3D.global_position, point, Color.RED)

“Enable the ray. Point it somewhere. Mask the right layers. Three things, and the ray works.”

Related Issues

For Area2D detection issues, see Godot Area2D Not Detecting StaticBody2D. For physics body movement problems, CharacterBody2D Floor Snap Not Working covers related collision setup.

enabled = true, target_position not zero, collision_mask matches target layer. That fixes 95% of raycast detection failures.