Quick answer: Capture TWeakObjectPtr<T> in your lambdas. Validate with IsValid() before dereferencing. AddToRoot only for pin-globally singletons; for transient async work, prefer weak.
You schedule an HTTP request that captures this as a raw pointer. The actor is destroyed before the response arrives. The callback dereferences the dead pointer; engine crashes.
The Symptom
Crash after a delay following async work. Sometimes the engine logs “dereferenced GC’d UObject” before the crash. Reproduces by destroying the calling actor before the async completes.
The Fix
Pattern: TWeakObjectPtr in capture.
TWeakObjectPtr<APlayer> WeakSelf = this;
Http->OnProcessRequestComplete().BindLambda([WeakSelf](FHttpRequestPtr Req, FHttpResponsePtr Resp, bool bOk)
{
APlayer* Self = WeakSelf.Get();
if (!Self || !Self->IsValidLowLevel())
{
UE_LOG(LogTemp, Warning, TEXT("Owner gone, dropping callback"));
return;
}
Self->HandleResponse(Resp, bOk);
});
WeakObjectPtr doesn’t keep the referent alive. Get() returns nullptr if it was GC’d. The early return saves you from the dead-pointer access.
UPROPERTY for Members
UObject member fields tagged UPROPERTY() keep their pointee alive while the owning object is alive:
UCLASS()
class AGameDirector : public AActor
{
UPROPERTY() AEnemy* CurrentEnemy; // kept alive
};
If you raw-pointer something, GC may collect it. Tag with UPROPERTY whenever the relationship matters.
AddToRoot Use Cases
Singleton-style UObjects (a content registry, a save manager) created at startup that should never be collected: AddToRoot. Pair with RemoveFromRoot in shutdown to allow clean teardown.
Verifying
Reproduce: spawn an actor, kick off async work, destroy the actor before completion. Should log “Owner gone” instead of crashing. Soak-test with many concurrent async kicks plus rapid spawn/destroy.
“Weak in lambdas. UPROPERTY in fields. Validate before deref. No mid-async crashes.”
Related Issues
For Blueprint async pending, see async task. For Replication Graph, see replication.
Weak captures. Validate. Lambdas survive.