When dereferencing a stale WeakPtr that is passed as reference into a blueprint function, the weak pointer is not actually resolved. Instead, the raw data of the weakptr are interpreted as if they were a pointer, resulting in almost certain crash. There is no validation that can prevent this in the target function.
WeakPtr when still valid, before passing to blueprints:
[Image Removed]
After incorrect resolve, this is what is passed into BP as an object pointer:
Hello! I was able to repro based on your info. You’ve uncovered a bug that is problematic even if the object was still alive. It looks like TWeakObjectPtr<UObject> being passed into a function as its UObject* argument by-ref (const or not), will simply copy the value’s bits onto the argument stack. The TWeakObjectPtr’s bitwise value (ObjectIndex, SerialIndex) is being reinterpreted as a UObject* memory address.
Since blueprints are on the way out, we won’t address this for UE 5.9 or UE6, but I can give you a engine modification to make to either (a) disallowing passing TWeakObjectPtr by-ref at editor-time, or (b) properly checking stalenesss and converting to a valid memory address at run-time.
For (a), in FKismetCompilerUtilities::IsTypeCompatibleWithProperty, add this section to the bottom inside the if (Property->HasAnyPropertyFlags(CPF_Parm) block:
// Disallow passing WeakObjectPtr by reference. Otherwise, currently its data would be reinterpreted as UObject* producing garbage.
if (Property->HasAnyPropertyFlags(CPF_ReferenceParm)
&& (SourcePin->Direction == EGPD_Input)
&& (CastField<FWeakObjectProperty>(Property) == nullptr))
{
for (const UEdGraphPin* LinkedPin : SourcePin->LinkedTo)
{
if (LinkedPin && LinkedPin->PinType.bIsWeakPointer && !LinkedPin->PinType.IsContainer())
{
MessageLog.Error(
*FText::Format(
LOCTEXT("WeakPtrToRefParam_ErrorFmt", "Cannot pass a weak object reference to by-reference parameter '{0}' of [Content removed]
FText::FromString(Property->GetName())
).ToString(),
SourcePin
);
break;
}
}
}
Going for solution (b) would provide some user friendliness by allowing passing TWeakObjectPtr by introducing an intermediate local variable (TWeakObjectPtr gets converted to UObject properly with staleness check, then passed into the function). However, for non-const references you would still need to copy mutated values back to the original graph so it’s non-trivial.
I recommend taking path (a) since passing TWeakObjectPtr implicitly as a UObject* is clearly unsupported currently, and preventing people from trying it rather than adding the feature support is the more future-proof solution since we won’t be adding the support ourselves at this stage.