Quick answer: set_deferred() queues the change until the end of the current frame, so reading the property right afterwards still returns the old value. The property name is a string that must match exactly (a typo is silently ignored), and physics properties such as monitoring or a shape's disabled affect collisions only from the next physics step.
You were told to use set_deferred() to change collision settings inside a physics callback, you did, and it looks like nothing happened. Usually it did happen — just later than you checked, or on a property name that does not exist.
When the value actually changes
Deferred calls are flushed when the engine is idle at the end of the frame. Code that runs right after the call still sees the old value:
$Area2D.set_deferred("monitoring", false)
print($Area2D.monitoring) # still true here
await get_tree().process_frame
print($Area2D.monitoring) # false now
So set_deferred() followed immediately by logic that depends on the new value is a bug. Move that logic into the deferred step too:
func _on_body_entered(body):
_disable_and_respawn.call_deferred()
func _disable_and_respawn():
$Hitbox.monitoring = false
respawn()
The property name must be exact
set_deferred("monitorng", false) does not error; it just sets nothing. Copy names from the Inspector (hover a property to see its script name) or the class reference. Note that nested values are not addressed with dots: to change a shape's disabled, call it on the CollisionShape2D node, not on the body.
$CollisionShape2D.set_deferred("disabled", true) # correct
set_deferred("CollisionShape2D:disabled", true) # not a property path
Physics changes need a physics step
Even after the deferred set runs, the physics server picks the change up on the next physics step. An Area turned off this frame can still report overlaps that were already queued. If you must be certain, wait for get_tree().physics_frame before relying on it.
Why you need it at all
Inside signals emitted by the physics server (body_entered, area_entered, _integrate_forces and similar), the server is iterating its data. Changing shapes, monitoring or collision layers directly can fail with "Can't change this state while flushing queries" or "Function blocked during in/out signal". Deferring the change avoids that. Outside physics callbacks, a direct assignment is fine and clearer.
The node is freed first
If the object is freed before the end of the frame, the deferred set is discarded. That is harmless, but it means a queue_free() plus set_deferred() on the same node does nothing useful.
Multiple changes
For several related changes, one deferred function is easier to reason about than a chain of set_deferred() calls, and guarantees they apply together.