Hello! We’re currently in a situation where we have set up many instances of a data asset type already configured for various features, only to realize that we would like to have blueprint inheritance to improve data maintainability/extensibility.
One approach that we’re investigating resolves around marking the data asset type as Blueprintable, creating a base blueprint class from that native data asset type, and then creating data assets derived from that base blueprint class. This seems to work well enough in practice, and we’re trying to use the context menu option ‘Convert to Different DataAsset Type’ (which runs ExecuteChangeDataAssetClass in AssetDefinition_DataAsset) to reparent existing data assets over to a corresponding base class. However, the results of this operation seem to vary:
Sometimes, it works exactly as expected
Sometimes, it appears to succeed up until the data asset is saved. Upon doing so, closing and reopening the asset editor reveals that it still derives from the native type instead of the blueprint class
Sometimes, it fails quite explicitly with the error “One or more assets were partially consolidated, yet still cannot be deleted for some reason” (see attached screenshot).
If I ignore the error and save the asset anyway, the outcome is otherwise similar to the previous point
I’m not sure if this is a red herring, but after having tested this out multiple times from an editor cold boot, the failure cases don’t seem to happen if the editor is left running idle for a few minutes before attempting the conversion. Perhaps this could indicate some sort of loading or timing issue?
In any case, my questions are as follows:
Is there anything we can do on our end to resolve this?
Is what we’re trying to do a bad idea™? Should we aim to remove data assets from the equation altogether? We’re a bit worried that this may involve quite a big refactor.
Just for my understanding you are creating a blueprint base of a data asset, and then converting existing Data assets that are based on a native C++ class to inherit a blueprint version of the asset. Both the old Data Assets and the Blueprint of the data asset inherit the same native C++ class.[Image Removed]I have replicated a setup similiar to this in a test project, and I have not been able to replicate the exact issues you are having. I have noticed some oddities within the content browser, with the preview, name and having to resave an asset in order to reference it.
The error in your screen shot suggests that the enginge could not rename the redirector back to the original object’s name. This indicates the original object could not be garbage collected even though DeleteSingleObject returned true.
The intended method to achieve this if you wanted complictaed data inheritance would be to use data-only bluyeprints. (Would also make them diff-able, within the engine)
You could facilitate this by creating an editor utility that would convert the data assets to a new type of UObject, copy the member data across etc.
However fixing up the references to the data assets might be trickier, and updating code to reflect the new asset type.
You could start by marking the old data type as deprecated to illuminate where its being used.
I think that would give you the most stable codebase to work from.
If there is any other guidence to automating the conversion process or any other questions please let me know.
Yes it is possible a large amount of data, the types of data wiuthin the data aseets and, if there is interconencted data assets referencing one another. Which may leave behind a redirector.
Is there already a redirector there, Have you tired running fix up redirectors across the project first, before attempting the conversion?
Are there any other errors in the Log file just before it hits the critical failure?
My initial thoughts would be to create UObject versions of each of the data assets, keeping the same inheritance structure as the UDataAssets.
Then create an editor utility which can loop through all your data assets and see if they are a type of UDerivedDataAsset1 map it to the new uobject type, and call a c++ blueprint function librayr which will follow pretty much the save code as ExecuteChangeDataAssetClass, with changes to how its spawning the blueprint version of the new class.
I’ll do some more investigating into what that code will look like and get back to you.
HI, From investigating further, I dont belive is is a supported workflow for data objects.
You would like to continue to use investigate data inheritance, I htink the best path forward would be to convert the assets to data only blueprints.
This could be achieved via an editor asset action, where you iterate the selected assets, create a new blueprint asset based of a parent class (you could create a map that maps from the native data asset class -> the new blueprint class), They copy the variables over (Found a forum post that appears to do this).
Hi Keegan, so while trying to write a tool to convert the data asset to blueprint I was running into a similar error, so I decided to debug it further and found a fix for one of the 2 issues:
> Sometimes, it fails quite explicitly with the error “One or more assets were partially consolidated, yet still cannot be deleted for some reason” (see attached screenshot).
TLDR Fix: Within MenuExtension_DataAsset::ExecuteChangeDataAssetClass add the following:
for (TWeakObjectPtr DataAssetPtr : DataAssets)
{
// …`Reason: The data assets that were triggering the error had the flag EInternalObjectFlags::LoaderImport present on them, such that when ObjectTools::CleanupAfterSuccessfulDelete tries to invoke garbage collection on them, they get ignored from consideration. FlushAsyncLoading forces them to be loaded before we try consolidating them, which prevents the object redirector from having a naming clash down the line.
I do think it would be more robust if ObjectTools methods had a way of identifying the async package id for the object and flushing async loading specifically that object only. That would be more performant and would avoid the need for calling clients to perform this step prior to consolidating objects.
With all that said, the other issue is still a factor and I haven’t found a fix for it, and will probably not investigate it in favor of switching over to use data-only blueprint classes instead:
> Sometimes, it appears to succeed up until the data asset is saved. Upon doing so, closing and reopening the asset editor reveals that it still derives from the native type instead of the blueprint class
Hey Keegan, your understanding is correct, and indeed I noticed the same content browser oddities you brought up (I had neglected to mention those as they seemed cosmetic in nature, and that issue went away if I navigated to a different folder and went back).
I too was unable to reproduce the issue on a vanilla instance of Unreal with an isolated test case. I apologize that I’m unable to narrow this down, but is it possible that the issue requires a large amount of data already configured in order for it to trigger?
We’re open to trying out a conversion from data asset to data-only blueprints, but there is a lot of data to cover, and we also have multiple native derivations of the base type to account for (ie. UDerivedDataAsset1, UDerivedDataAsset2, etc that inherit from UBaseDataAsset). Any guidance on how to handle this cleanly would be very helpful for us!
> Is there already a redirector there, Have you tired running fix up redirectors across the project first, before attempting the conversion?
There is no redirector already present. In this case it’s failing to delete the one that was dynamically created under the hood during the conversion attempt.
> Are there any other errors in the Log file just before it hits the critical failure?
I’m having some trouble reproing the error message, but for the time being I’ve grabbed some logs for the other failure case (“Sometimes, it appears to succeed up until the data asset is saved. Upon doing so, closing and reopening the asset editor reveals that it still derives from the native type instead of the blueprint class”). Please note that I’ve redacted some asset names and paths to better match your example (and for confidentiality reasons):
`// NOTE: No relevant logs present when I right-click → Convert to Different DataAsset Type, the first one only appears when I open the asset editor to save the data asset
[2025.05.26-15.06.17:026][253]LogAssetEditorSubsystem: Opening Asset editor for BP_Base_FromNativeDataAsset_C /Game/Path/To/Asset/DA_Child_FromBP.DA_Child_FromBP
[2025.05.26-15.06.17:070][253]LogBlueprintAssist: Asset opened DA_Child_FromBP (GenericAssetEditor)
[2025.05.26-15.06.21:939][297]LogLinker: Conditionally flushing loading for linker(s) (/Game/Path/To/Asset/DA_Child_FromBP)
[2025.05.26-15.06.21:941][297]OBJ SavePackage: Generating thumbnails for [0] asset(s) in package [/Game/Path/To/Asset/DA_Child_FromBP] ([1] browsable assets)…
[2025.05.26-15.06.21:941][297]OBJ SavePackage: Finished generating thumbnails for package [/Game/Path/To/Asset/DA_Child_FromBP]
[2025.05.26-15.06.21:942][297]Cmd: OBJ SAVEPACKAGE PACKAGE=“/Game/Path/To/Asset/DA_Child_FromBP” FILE=“../../../ProjectName/Content/Path/To/Asset/DA_Child_FromBP.uasset” SILENT=true
[2025.05.26-15.06.21:950][297]LogSavePackage: Moving output files for package: /Game/Path/To/Asset/DA_Child_FromBP
[2025.05.26-15.06.21:951][297]LogSavePackage: Moving ‘../../../ProjectName/Saved/DA_Child_FromBP9CC1D16A41DAC3C4F9C06B9922EB0E56.tmp’ to ‘../../../ProjectName/Content/Path/To/Asset/DA_Child_FromBP.uasset’
[2025.05.26-15.06.22:031][297]AssetCheck: /Game/Path/To/Asset/DA_Child_FromBP Validating asset
[2025.05.26-15.06.22:878][326]LogSlate: Window ‘DA_Child_FromBP’ being destroyed
[2025.05.26-15.06.22:883][326]LogSlate: Window ‘DA_Child_FromBP’ being destroyed
[2025.05.26-15.06.22:915][326]LogBlueprintAssist: Asset closed DA_Child_FromBP
[2025.05.26-15.06.24:719][422]LogAssetEditorSubsystem: Opening Asset editor for NativeDataAsset /Game/Path/To/Asset/DA_Child_FromBP.DA_Child_FromBP
[2025.05.26-15.06.24:780][422]LogBlueprintAssist: Asset opened DA_Child_FromBP (GenericAssetEditor)
[2025.05.26-15.06.27:411][552]LogSlate: Window ‘DA_Child_FromBP’ being destroyed
[2025.05.26-15.06.27:418][552]LogSlate: Window ‘DA_Child_FromBP’ being destroyed
[2025.05.26-15.06.27:449][552]LogBlueprintAssist: Asset closed DA_Child_FromBP`Of particular note, observe how the first LogAssetEditorSubsystem message refers to its base type as BP_Base_FromNativeDataAsset_C, but when I save->close->reopen the data asset, the second time around the LogAssetEditorSubsystem message refers to its base type as NativeDataAsset.
> I do think it would be more robust if ObjectTools methods had a way of identifying the async package id for the object and flushing async loading specifically that object only. That would be more performant and would avoid the need for calling clients to perform this step prior to consolidating objects.
I stumbled across ConditionalFlushAsyncLoadingForSave today which seems to do just that, so I imagine it may be possible to achieve this.