Hey, thanks for the repro code. This issue is still present in UE 5.7+ and is primarily an oversight that comes from TOptional’s editor logic differing from other property types. I’ve entered this as bug UE-350636 in our issue tracker. You can keep an eye on that page for progress on the engine fix.
Here is what I’ve found so far. The problem starts when setting/unsetting: the editor path for settingand unsetting the optional value does not propagate the set/unset state to any children. That logic is specific to TOptional and is currently flawed. Callstacks for reference:
OptionalProperty->MarkSetAndGetInitializedValuePointerToReplace(Addr);
> UnrealEditor-PropertyEditor.dll!FPropertyHandleOptional::SetOptionalValue(FProperty * NewProperty, const UClass * NewObjectClass) Line 5713 C++
UnrealEditor-PropertyEditor.dll!FPropertyEditor::OnSetOptionalValue(FProperty * NewProperty, const UClass * NewObjectClass) Line 374 C++
OptionalProperty->MarkUnset(Addr);
> UnrealEditor-PropertyEditor.dll!FPropertyHandleOptional::ClearOptionalValue() Line 5867 C++
UnrealEditor-PropertyEditor.dll!FPropertyEditor::OnClearOptionalValue() Line 389 C++
I have a half-solution which at least updates the data of loaded children immediately. It doesn’t refresh opened blueprint editors so their view is incorrect until blueprint compilation. Just sharing this in case you want to take the half-solution already to mitigate data loss. In FPropertyHandleOptional::SetOptionalValue() you can make the following modification:
OptionalProperty->MarkSetAndGetInitializedValuePointerToReplace(Addr);
// START EDIT
const TArray<UObject*>& AffectedInstances = AffectedInstancesPerObject[i];
for (UObject* Inst : AffectedInstances)
{
void* InstPropAddr = OptionalProperty->ContainerPtrToValuePtr<void>(Inst);
OptionalProperty->MarkSetAndGetInitializedValuePointerToReplace(InstPropAddr);
UE_LOG(LogTemp, Warning, TEXT("Additionally set and initialized optional value on: %s"), *Inst->GetPathName());
}
// END EDIT
Similarly in ClearOptionalValue:
OptionalProperty->MarkUnset(Addr);
// START EDIT
const TArray<UObject*>& AffectedInstances = AffectedInstancesPerObject[i];
for (UObject* Inst : AffectedInstances)
{
void* InstPropAddr = OptionalProperty->ContainerPtrToValuePtr<void>(Inst);
OptionalProperty->MarkUnset(InstPropAddr);
UE_LOG(LogTemp, Warning, TEXT("Additionally unset optional value on: %s"), *Inst->GetPathName());
}
// END EDIT
The details view of derived BPs will be outdated, but any view refresh like on compiling blueprints will correct that. With this solution, at least resaving derived BPs after making a parent BP change will no longer cause them to stop inheriting the value. The full fix will follow later, but hard to say when.