Unreal Engine: Fixing the ‘Pin Context Object must have a connection’ Issue

I wanted to empower my sound designer to create simple blueprints that respond to Game Events. To achieve this, I created a custom class inheriting from a UObject to encapsulate the blueprint script. However, in this object blueprint, when attempting to access a Service (or any static method with a hidden World Context Object pin), an error occurs: Class X is unsafe to call from blueprint of class Y. Pin Context Object must have a connection.

This issue arises because, unlike actors, the UObject is not inherently parented to a World. Consequently, a hidden pin that is usually set automatically remains unset this time.
There are two possible solutions:

  1. Display the Pin and Connect Something Else
  2. Parent Your Object to a World

1. Display the Pin and Connect Something Else

In this solution, simply add the UCLASS property meta = (ShowWorldContextPin) on top of the class executing the blueprint script.

UCLASS(Blueprintable, BlueprintType, meta = (ShowWorldContextPin))
class THEPATHTOL0_API UL0GameEventCue : public UObject
{
    UFUNCTION(BlueprintImplementableEvent, BlueprintCallable)
    void Execute(UWorld* World) const;
}

Now the pin appears. All you need to do is add any World Context Object to the function from the caller. In the example, I pass a UWorld, but it could be an actor, for instance.

2. Parent Your Object to a World

The other solution is to parent the UObject to a class. To achieve this, make sure to have a pointer to an actor parented to a World or have a pointer to a world. This is the case if you call it from a Subsystem (UGameInstanceSubsystem returns a valid world from GetWorld().

First, override the GetWorld() function. It’s crucial to do this and not call Super::GetWorld(); there is a check in this class, and it won’t work:

UWorld* UL0GameEventCue::GetWorld() const
{
    // We may be called from a Subservice which is not a good candidate to be an Outer object. However UGameInstanceSubsystem have a valid GetWorld().
    // Thus the Outer might be directly the world.
    if (UWorld* WorldOuter = Cast<UWorld>(GetOuter()))
    {
        return WorldOuter;
    }

    const AActor* Outer = Cast<AActor>(GetOuter());
    if(Outer)
    {
        return Outer->GetWorld();
    }

    return nullptr;
}

Additionally, ensure that when you instantiate the object, you pass a valid Outer Object (and not the transient package, for instance):

UObject* Outer = this; // This is an Actor
// UObject* Outer = GetWorld(); // Or use a GetWorld() accessor. Ensure that it returns a valid pointer.
UL0GameEventCue* CueInstance = NewObject<UL0GameEventCue>(Outer, Element.Get());

Now it should work smoothly.

If you encounter the error PIE: Error: Blueprint Runtime Error: PIE: Error: Blueprint Runtime Error: "Accessed None trying to read property XXX, it means that your GetWorld() function returns null. Recheck the outer object you passed in and the body of the GetWorld function.