Quick answer: Move PhysicsServer3D / direct space state queries to _physics_process. For per-body forces, use the RigidBody3D _integrate_forces override. Direct state outside the physics step is invalid.

Custom raycast in _process. Editor warning, sometimes a crash. Direct state queries are only valid during the physics step.

The Symptom

Crash or assertion: “PhysicsDirectBodyState3D not in valid state.” Or queries return nonsense (zero positions, infinite velocities). Always reproduces on the second physics frame.

The Fix

extends Node3D

func _physics_process(_delta: float) -> void:
    var space := get_world_3d().direct_space_state
    var q := PhysicsRayQueryParameters3D.create(global_position, global_position + Vector3.DOWN * 5)
    var hit := space.intersect_ray(q)
    if hit:
        print("Hit at ", hit.position)

direct_space_state is fresh and valid only inside _physics_process.

RigidBody3D Custom Forces

extends RigidBody3D

func _integrate_forces(state: PhysicsDirectBodyState3D) -> void:
    # state is valid here
    var v := state.linear_velocity
    state.linear_velocity = v * 0.99   # custom drag
    state.apply_central_force(Vector3.UP * 9.8 * mass)

_integrate_forces receives the live PhysicsDirectBodyState3D.

If You Must Query in _process

Cache the latest physics state in a member variable during _physics_process. Read the cache from _process. Stale by up to one physics tick but doesn’t crash.

Verifying

Move queries to _physics_process. Crash disappears. Hit data is consistent. Frame-time impact is minimal because _physics_process runs at a fixed rate.

“Physics queries in physics callbacks. Direct state stays valid.”

Related Issues

For physics interpolation jitter, see interpolation. For Area3D detection, see Area3D.

_physics_process for queries. State holds.