I have a UBlueprintAsyncActionBase-derived proxy class with two BlueprintAssignable multicast delegates :
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(
FMyCompletedDelegate,
const TArray<float>&, Output);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(
FMyFailedDelegate,
const FString&, Reason);
UCLASS()
class MYMODULE_API UMyAsyncAction : public UBlueprintAsyncActionBase
{
GENERATED_BODY()
public:
UPROPERTY(BlueprintAssignable)
FMyCompletedDelegate OnCompleted;
UPROPERTY(BlueprintAssignable)
FMyFailedDelegate OnFailed;
UFUNCTION(BlueprintCallable, Category="MyCategory",
meta=(BlueprintInternalUseOnly="true",
WorldContext="WorldContextObject",
DisplayName="My Async Action"))
static UMyAsyncAction* MyAsyncAction(
UObject* WorldContextObject, ...);
virtual void Activate() override;
};
The generated K2Node in Blueprint shows :
-
On Completedexec pin +Outputdata pin (TArray)
-
On Failedexec pin WITHOUT anyReasondata pin
The OnFailed exec pin works (firing, broadcast received, downstream nodes execute), but the Reason parameter is invisible to BP.
What I have verified :
-
UHT generates both delegate signature functions correctly :
obj list class=Function name=FMyCompletedDelegate__DelegateSignature -> 1 instanceobj list class=Function name=FMyFailedDelegate__DelegateSignature -> 1 instance -
Only one instance of the proxy class loaded (no
REINST_*orHOTRELOADED_*duplicates) :obj list class=Class name=MyAsyncAction -> 1 instance -
The
.gen.cppregistersOnFailedasBlueprintAssignablewith identical flags toOnCompleted:NewProp_OnCompleted flags = 0x0010000010080000NewProp_OnFailed flags = 0x0010000010080000 (identical) -
The difference appears in the delegate signature property/function flags :
FMyCompletedDelegate (TArray param) :NewProp_Output = 0x0010000008000182 (Parm | OutParm | RefParm | ConstParm | NativeAccessPublic)FuncParams = 0x00530000 (HasOutParms | Public | MulticastDelegate | Delegate)FMyFailedDelegate (FString param) :NewProp_Reason = 0x0010000000000080 (only Parm | NativeAccessPublic)FuncParams = 0x00130000 (no HasOutParms)UHT does not promote
const FString&to a ref/out param in delegate signatures, while it does forTArray<T>&.
What I have already tried :
-
const FString&→ no pin -
FString(by value) → no pin -
UPARAM(ref) const FString&→ does not compile (UPARAM is not accepted insideDECLARE_DYNAMIC_MULTICAST_DELEGATE_*macros) -
Manually patching the
.gen.cppto set0x0010000008000182onNewProp_Reasonand0x00530000onFuncParams(matching the working TArray case), then rebuilding the.dll→ still no pin -
Renaming the UPROPERTY from
OnErrortoOnFailed→ no effect -
Full clean rebuild (deleted
Binaries/andIntermediate/), full editor restart, fresh BP from scratch → still no pin
The manual flag patch is what I find most surprising : I confirmed via .dll timestamp + obj list that the patched binary was actually loaded, and the K2Node still does not generate the Reason pin even though the property and function flags are now byte-identical to the working Output/OnCompleted case.
The only difference left between the two cases is the property type itself : FArrayProperty (works) vs FStrProperty (does not work).
My questions :
-
Is there a known UE 5.7 limitation or behavior in
UK2Node_BaseAsyncTask::CreatePinsForClass(or downstream pin-generation logic) that filters outFStrPropertyparameters from delegate signatures, even when they have the standard out/ref/const flags? -
If yes, is there a way to declare a
BlueprintAssignabledelegate with aFStringparameter that does produce a BP pin, without wrapping it in aUSTRUCT? -
If a
USTRUCTwrapper is the only way, is this a known/documented limitation or just an empirical workaround the community has converged on?
Workaround I plan to apply : wrap the FString in a USTRUCT(BlueprintType). Confirming this is the right path before changing the API.
Engine version : UE 5.7