UAssetManager::ChangeBundleStateForPrimaryAssets is not following redirectors when building PathsToLoad

Recently we moved a class and added a core redirect for that class. Pretty much everything worked as expected like normal with the redirector apart from one very annoying randomly error we were getting. Below you can see the redirect we added which in most cases, worked just fine.

+ClassRedirects=(OldName=“/Script/Atlas.InventoryComponent”,NewName=“/Script/CCPGameplayInventory.InventoryComponent”)

The error we were seeing randomly, was as follows.

LogUObjectGlobals: Warning: Failed to find object ‘Object /Script/Atlas.InventoryComponent’

That’s all we had to go on, no further information. Given that this was inconsistent to repro, some machines it worked fine and others not, it took a while before we got a semi stable repro to debug against. So, I added the following debug.

LogCook: Display: Waiting for Asset Registry

LogTemp: Display: TOMW-1: /Game/Mining/Data/Items/Live/Weapons/ID_Weapon_MiningTool.ID_Weapon_MiningTool

LogTemp: Display: TOMW-2: /Game/Mining/Data/Items/Live/Weapons/ID_Weapon_MiningTool.ID_Weapon_MiningTool

LogTemp: Display: TOMW-2: /Game/UI/Common/Textures/InventoryIcons/Weapons/T_AMARRAshbornPDW_WeaponIcon_SM.T_AMARRAshbornPDW_WeaponIcon_SM

LogTemp: Display: TOMW-2: /Game/UI/HUD/Equipment/Textures/TUI_MiningLaser_Active.TUI_MiningLaser_Active

LogTemp: Display: TOMW-2: /Game/Mining/Data/Equipment/Tools/EqD_MiningTool_Main.EqD_MiningTool_Main

LogTemp: Display: TOMW-2: /Game/Mining/Data/Equipment/Tools/EqD_MiningTool_Shelved.EqD_MiningTool_Shelved

LogTemp: Display: TOMW-2: /Script/Atlas.InventoryComponent

LogTemp: Display: TOMW-2: /Game/Data/Inventory/Weapons/Extensions/InvExtD_MiningTool_Firmware.InvExtD_MiningTool_Firmware

LogTemp: Display: TOMW-2: /Game/Mining/Data/Inventory/Extensions/InvExtD_MiningTool_Magazine.InvExtD_MiningTool_Magazine

LogUObjectGlobals: Warning: Failed to find object ‘Object /Script/Atlas.InventoryComponent’

As you can see highlighted, the ID_Weapon_MiningTool asset is still trying to load the unredirected class path. The request to the unredirected class is coming from AssetManager.cpp LN 1854 where it issues a load request with an array of PathsToLoad.

This seems to be to be a fault of the AssetManager in how it’s building the PathsToLoad array, it does not seem to be resolving redirectors and is just trying to load whatever reference is there. My suggested fix would be to simply call something like FixupRedirectedAssetPath when building the PathsToLoad array so that redirects are correctly followed.

  1. What is the expected way that redirects inside bundles are meant to be resolved? Is adding FixupRedirectedAssetPath correct?
  2. Why is this issue intermitent? What updates the redirection in a bundle? It seems like some machines had state where the redirector had been applied but others did not.
  3. This was a pain to debug, can you/we add some kind of debug to highlight this issue more clearly?

See below where I added FixupRedirectedAssetPath (denoted by CCP_MOD*)* to find the redirected path to any assets referenced by bundles.

`TArray PathsToLoad;

// Gather asset refs
// <#CCP_MOD>
//
// Follow redirects to ensure we load the correct asset.
//
// UDN: [Content removed]
//
FSoftObjectPath FinalRootPath(NameData->GetAssetPtr().ToSoftObjectPath());
UAssetRegistryHelpers::FixupRedirectedAssetPath(FinalRootPath);

const FSoftObjectPath& AssetPath = FinalRootPath;
// </#CCP_MOD>

if (!AssetPath.IsNull())
{
// Dynamic types can have no base asset path
PathsToLoad.Add(AssetPath);
}

for (const FName& BundleName : NewBundleState)
{
FAssetBundleEntry Entry = GetAssetBundleEntry(PrimaryAssetId, BundleName);

if (Entry.IsValid())
{
for (const FTopLevelAssetPath & Path : Entry.AssetPaths)
{
// <#CCP_MOD>
//
// Follow redirects to ensure we load the correct asset.
//
// UDN: [Content removed]
//
FSoftObjectPath FinalBundlePath(Path);
UAssetRegistryHelpers::FixupRedirectedAssetPath(FinalBundlePath);

PathsToLoad.AddUnique(FinalBundlePath);
// </#CCP_MOD>
}
}
else
{
UE_LOG(LogAssetManager, Verbose, TEXT(“ChangeBundleStateForPrimaryAssets: No assets for bundle %s::%s”), *PrimaryAssetId.ToString(), *BundleName.ToString());
}
}`

Some additional notes…

  1. Is there a cleaner way to resolve these redirects or is using UAssetRegistryHelpers::FixupRedirectedAssetPath correct?
    1. It seems like there are two redirection resolvers, CoreRedirects and the AssetManager redirects, either of which could be active.
  2. You probably want to delete this? I noticed it while in the AssetManager.
    1. //UE_LOG(LogTemp, Warning, TEXT(“pooo %d”), MoviePlayerNumAssets);

Hi Thomas,

I’ll first try to answer your questions in order, and then ask some clarifying questions of my own:

‘What is the expected way that redirects inside bundles are meant to be resolved? Is adding FixupRedirectedAssetPath correct?’

Redirects should be handled by the lower-level loading systems so using FixupRedirectedAssetPath at that point is not appropriate.

‘Why is this issue intermittent? What updates the redirection in a bundle? It seems like some machines had state where the redirector had been applied but others did not.’

I’m not sure about this, but it could be related to the order that objects/assets are loaded. It requires some further debugging and context.

‘This was a pain to debug, can you/we add some kind of debug to highlight this issue more clearly?’

We will have a look to see how we can improve the reporting. From the log you posted it looks like the warning is coming from StaticLoadObject (UObjectGlobals.cpp) so it looks like ResolveName2 is not redirecting correctly. Let us know if you uncover anything more so that we can see where it makes sense to add more logging/context.

‘Is there a cleaner way to resolve these redirects or is using UAssetRegistryHelpers::FixupRedirectedAssetPath correct?’

Redirection should be handled already by the lower level loading systems so you shouldn’t have to handle it.

‘You probably want to delete this? I noticed it while in the AssetManager.’

Thank you, it’s now deleted.

Some questions:

  • Are you experiencing the issue in an Editor or cooked build?
    • In a cooked build some of the redirect handling is disabled because the assumption is the cook process will re-save everything.
  • How is /Script/Atlas.InventoryComponent referenced?
    • For example is it referenced via a TSoftClassPtr?

Thank you for the additional info.

As a workaround could you modify UItemDefinition::UpdateAssetBundleData so that UAssetRegistryHelpers::FixupRedirectedAssetPath is called there instead?

FixupRedirectedAssetPath is a pretty slow function and ChangeBundleStateForPrimaryAssets in the AssetManager is called frequently, while UpdateAssetBundleData is only called at save and cook time.

Apologies I didn’t mean that the workaround I suggested is a solution, just an alternative in case you were planning to go ahead with your change in ChangeBundleStateForPrimaryAssets.

Redirects should be supported, though. From your initial message, I understood that the problem is intermittent not that the redirects are not working on AssetBundles at all.

To debug the issue further, you can try running with: -DebugCoreRedirects

This should log when every redirect is added. It should also tell you the .ini file it came from and perform some additional validation.

It would be interesting to see the output from that log when the error happens and check if the ClassRedirects for: InventoryComponent was added.

You can also use -FullDebugCoreRedirects, but that might log more info than you need to debug this.

“What is the correct way to handle redirectors for assets referenced in bundles?”

They should be handled for you by the CoreRedirects and the loading system. You shouldn’t have to handle them explicitly.

"I see that FixupRedirectedAssetPath is used reasonably extensively in the Engine at runtime in other locations, like World Parition for example, are those usages incorrect as well?"

FixupRedirectedAssetPath checks both CoreRedirects and the UObjectRedirector list maintained by the asset registry. In the beginning of UE4 only manual UObjectRedirector lookup worked fully, so some of those cases that you see are there for legacy reasons.

I have managed to reproduce the core issue, which is actually in FStreamableManager (which asset manager calls to do the actual loading). The problem is that it’s not calling FixupCoreRedirects when failing to load a path. The loading calls on FSoftObjectPath itself already handle this properly, so I have filed UE-296063 to fix the issue properly. Nick and I will discuss the right way to resolve that.

In the meantime, you should be able to simplify your workaround by only calling FinalBundlePath.FixupCoreRedirects() instead of the slow FixupRedirectedAssetPath (which also looks things up in the asset registry). Or, if you resave the original asset that had the issue in the editor it should go away as that also calls FixupCoreRedirects. I think that might be why it was randomly happening, sometimes the cooker can save an asset multiple times and that probably fixed up the reference.

“Are you experiencing the issue in an Editor or cooked build?”

Editor, when cooking using the following parameters or when running commandlets.

Running: UnrealEditor-Cmd.exe “ProjectNameGoesHere” -run=Cook -TargetPlatform=LinuxServer -buildmachine -unversioned -fileopenlog -abslog=“LogPathGoesHere.txt” -stdout -CrashForUAT -unattended -NoLogTimes -buildmachine -UTF8Output

“How is /Script/Atlas.InventoryComponent referenced?”

Via a TObjectPtr<> which is populated within UpdateAssetBundleData() which is an overriden function from UPrimaryDataAsset.

UPROPERTY(BlueprintReadOnly, EditDefaultsOnly, Category = “Gameplay”, Instanced, meta = (ShowInnerProperties, NoElementDuplicate))

TArray<TObjectPtr<UItemFragment>> ItemFragments;

`#if WITH_EDITORONLY_DATA
void UItemDefinition::UpdateAssetBundleData()
{
Super::UpdateAssetBundleData();

for (UItemFragment* ItemFragment : ItemFragments)
{
if (ItemFragment)
{
ItemFragment->AddAdditionalAssetBundleData(AssetBundleData);
}
}
}
#endif // WITH_EDITORONLY_DATA`“Let us know if you uncover anything more so that we can see where it makes sense to add more logging/context.”

I added some debug to print out the full callstack to show the path the error is taking, as shown below. One key thing to note is that we have our own asset manager implementation UCCPAssetManager which does some basic logic but does take action during UAssetManager::OnAssetRegistryFilesLoaded, as shown below.

LogUObjectGlobals: Warning: Failed to find object 'Object /Script/Atlas.InventoryComponent' LogOutputDevice: Error: === Handled ensure: === LogOutputDevice: Error: LogOutputDevice: Error: Ensure condition failed: Existing->Target [File:C:\BuildAgent\work\aeb477b3d20c2c2c\Engine\Source\Runtime\Engine\Private\StreamableManager.cpp] [Line: 1343] LogOutputDevice: Error: LogOutputDevice: Error: Stack: LogOutputDevice: Error: [Callstack] 0x00007ffcbd399922 UnrealEditor-Engine.dll!FStreamableManager::StreamInternal() [C:\BuildAgent\work\aeb477b3d20c2c2c\Engine\Source\Runtime\Engine\Private\StreamableManager.cpp:1343] LogOutputDevice: Error: [Callstack] 0x00007ffcbd398327 UnrealEditor-Engine.dll!FStreamableManager::StartHandleRequests() [C:\BuildAgent\work\aeb477b3d20c2c2c\Engine\Source\Runtime\Engine\Private\StreamableManager.cpp:1562] LogOutputDevice: Error: [Callstack] 0x00007ffcbd39149c UnrealEditor-Engine.dll!FStreamableManager::RequestAsyncLoadInternal() [C:\BuildAgent\work\aeb477b3d20c2c2c\Engine\Source\Runtime\Engine\Private\StreamableManager.cpp:1527] LogOutputDevice: Error: [Callstack] 0x00007ffcbd391637 UnrealEditor-Engine.dll!FStreamableManager::RequestSyncLoadInternal() [C:\BuildAgent\work\aeb477b3d20c2c2c\Engine\Source\Runtime\Engine\Private\StreamableManager.cpp:1550] LogOutputDevice: Error: [Callstack] 0x00007ffcbb88bbc1 UnrealEditor-Engine.dll!UAssetManager::LoadAssetListInternal() [C:\BuildAgent\work\aeb477b3d20c2c2c\Engine\Source\Runtime\Engine\Private\AssetManager.cpp:2279] LogOutputDevice: Error: [Callstack] 0x00007ffcbb84a3e5 UnrealEditor-Engine.dll!UAssetManager::ChangeBundleStateForPrimaryAssets() [C:\BuildAgent\work\aeb477b3d20c2c2c\Engine\Source\Runtime\Engine\Private\AssetManager.cpp:1854] LogOutputDevice: Error: [Callstack] 0x00007ffcbb88bffa UnrealEditor-Engine.dll!UAssetManager::LoadPrimaryAssets() [C:\BuildAgent\work\aeb477b3d20c2c2c\Engine\Source\Runtime\Engine\Private\AssetManager.cpp:2103] LogOutputDevice: Error: [Callstack] 0x00007ffc99cf54ad UnrealEditor-CCPCoreRuntime.dll!UCCPAssetManager::PostInitialAssetScanOfType() [C:\BuildAgent\work\aeb477b3d20c2c2c\Atlas\Plugins\CCPCore\Source\CCPCoreRuntime\Private\Systems\CCPAssetManager.cpp:178] LogOutputDevice: Error: [Callstack] 0x0000024f3b691450 UnrealEditor-Atlas.dll!UAtlasAssetManager::PostInitialAssetScan() [C:\BuildAgent\work\aeb477b3d20c2c2c\Atlas\Source\Atlas\Systems\AtlasAssetManager.cpp:63] LogOutputDevice: Error: [Callstack] 0x00007ffcbb890a8b UnrealEditor-Engine.dll!UAssetManager::OnAssetRegistryFilesLoaded() [C:\BuildAgent\work\aeb477b3d20c2c2c\Engine\Source\Runtime\Engine\Private\AssetManager.cpp:3979] LogOutputDevice: Error: [Callstack] 0x00007ffcbb85eeb7 UnrealEditor-Engine.dll!TBaseUObjectMethodDelegateInstance<0,UAssetManager,void __cdecl(void),FDefaultTSDelegateUserPolicy>::ExecuteIfSafe() [C:\BuildAgent\work\aeb477b3d20c2c2c\Engine\Source\Runtime\Core\Public\Delegates\DelegateInstancesImpl.h:667] LogOutputDevice: Error: [Callstack] 0x00007ffcb4795343 UnrealEditor-AssetRegistry.dll!TMulticastDelegate<void __cdecl(void),FDefaultTSDelegateUserPolicy>::Broadcast() [C:\BuildAgent\work\aeb477b3d20c2c2c\Engine\Source\Runtime\Core\Public\Delegates\DelegateSignatureImpl.inl:1079] LogOutputDevice: Error: [Callstack] 0x00007ffcb4796c85 UnrealEditor-AssetRegistry.dll!UAssetRegistryImpl::Broadcast() [C:\BuildAgent\work\aeb477b3d20c2c2c\Engine\Source\Runtime\AssetRegistry\Private\AssetRegistry.cpp:9285] LogOutputDevice: Error: [Callstack] 0x00007ffcb48022dc UnrealEditor-AssetRegistry.dll!UAssetRegistryImpl::WaitForCompletion() [C:\BuildAgent\work\aeb477b3d20c2c2c\Engine\Source\Runtime\AssetRegistry\Private\AssetRegistry.cpp:2069] LogOutputDevice: Error: [Callstack] 0x00007ffcb7a9a12e UnrealEditor-UnrealEd.dll!UCookOnTheFlyServer::BlockOnAssetRegistry() [C:\BuildAgent\work\aeb477b3d20c2c2c\Engine\Source\Editor\UnrealEd\Private\CookOnTheFlyServer.cpp:9050] LogOutputDevice: Error: [Callstack] 0x00007ffcb7b1d980 UnrealEditor-UnrealEd.dll!UCookOnTheFlyServer::StartCookByTheBook() [C:\BuildAgent\work\aeb477b3d20c2c2c\Engine\Source\Editor\UnrealEd\Private\CookOnTheFlyServer.cpp:11915] LogOutputDevice: Error: [Callstack] 0x00007ffcb77a9a1a UnrealEditor-UnrealEd.dll!UCookCommandlet::RunCookByTheBookCook() [C:\BuildAgent\work\aeb477b3d20c2c2c\Engine\Source\Editor\UnrealEd\Private\Commandlets\CookCommandlet.cpp:587] LogOutputDevice: Error: [Callstack] 0x00007ffcb7765387 UnrealEditor-UnrealEd.dll!UCookCommandlet::CookByTheBook() [C:\BuildAgent\work\aeb477b3d20c2c2c\Engine\Source\Editor\UnrealEd\Private\Commandlets\CookCommandlet.cpp:547] LogOutputDevice: Error: [Callstack] 0x00007ffcb77862d3 UnrealEditor-UnrealEd.dll!UCookCommandlet::Main() [C:\BuildAgent\work\aeb477b3d20c2c2c\Engine\Source\Editor\UnrealEd\Private\Commandlets\CookCommandlet.cpp:267] LogOutputDevice: Error: [Callstack] 0x00007ff6f004eb5e UnrealEditor-Cmd.exe!FEngineLoop::PreInitPostStartupScreen() [C:\BuildAgent\work\aeb477b3d20c2c2c\Engine\Source\Runtime\Launch\Private\LaunchEngineLoop.cpp:4154] LogOutputDevice: Error: [Callstack] 0x00007ff6f0045566 UnrealEditor-Cmd.exe!GuardedMain() [C:\BuildAgent\work\aeb477b3d20c2c2c\Engine\Source\Runtime\Launch\Private\Launch.cpp:144] LogOutputDevice: Error: [Callstack] 0x00007ff6f00458aa UnrealEditor-Cmd.exe!GuardedMainWrapper() [C:\BuildAgent\work\aeb477b3d20c2c2c\Engine\Source\Runtime\Launch\Private\Windows\LaunchWindows.cpp:123] LogOutputDevice: Error: [Callstack] 0x00007ff6f0049124 UnrealEditor-Cmd.exe!LaunchWindowsStartup() [C:\BuildAgent\work\aeb477b3d20c2c2c\Engine\Source\Runtime\Launch\Private\Windows\LaunchWindows.cpp:277] LogOutputDevice: Error: [Callstack] 0x00007ff6f005bd14 UnrealEditor-Cmd.exe!WinMain() [C:\BuildAgent\work\aeb477b3d20c2c2c\Engine\Source\Runtime\Launch\Private\Windows\LaunchWindows.cpp:317] LogOutputDevice: Error: [Callstack] 0x00007ff6f005e432 UnrealEditor-Cmd.exe!__scrt_common_main_seh() [D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:288] LogOutputDevice: Error: [Callstack] 0x00007ffcf8c2259d KERNEL32.DLL!UnknownFunction [] LogOutputDevice: Error: [Callstack] 0x00007ffcfa12af38 ntdll.dll!UnknownFunction []

We could do however, it just moves the problem somewhere else in the future and we’ll end up playing whack-a-mole fixing each individual issue one at a time and we’d end up in the same situation of having all bundle assets call UpdateAssetBundleData but we’d be doing it the long way around.

If there was some kind of validation that we could run during pre-submit tests that would catch this issue then we could potentially revert this change.

Additionally, I would add that bundles not supporting CoreRedirects or AssetRedirects is a very odd experience for the end user and feels wrong. It’s an entire fundamental system the bundles are not support and not providing an alternative for.

While I absolutely understand the desire to save time, the Engine is not providing a scalable solution by which we can work around this issue.

Even more additionally, the UGameFeatureData::UpdateAssetBundleData() function in the Engine does not apply this redirection so, it would suffer from the exact same problem so, it really feels like the standard expectation of how to redirect assets inside bundles is not well defined at an Engine level.

`#if WITH_EDITORONLY_DATA
void UGameFeatureData::UpdateAssetBundleData()
{
Super::UpdateAssetBundleData();

for (UGameFeatureAction* Action : Actions)
{
if (Action)
{
Action->AddAdditionalAssetBundleData(AssetBundleData);
}
}
}
#endif // WITH_EDITORONLY_DATA`

Ah, OK, that makes sense. To be honest, we’ve gone ahead with the change in ChangeBundleStateForPrimaryAssets already since it was blocking us and we’ve not really noticed any change in cooking or runtime performance so far.

I believe the source of the intermitent nature of this is due to build machines performing cooks over time and that somehow resolving this issue, it’s not really clear why though.

I see that FixupRedirectedAssetPath is used reasonably extensively in the Engine at runtime in other locations, like World Parition for example, are those usages incorrect as well? Not to mention the underlying FixupCoreRedirects() is called in some seriously deep Engine classes.

It feels to me like while the concept of redirectors exist then bundles should handle the redirect, it seems more like the issue is that the redirection itself could be slow so the optimisation effort should be towards making the efficient.

I guess ultimately what I am asking for here is…

“What is the correct way to handle redirectors for assets referenced in bundles?”

I will try to get you the data but it might take a while.

“They should be handled for you by the CoreRedirects and the loading system. You shouldn’t have to handle them explicitly.”

Where and how should this be happening? I am happy to add it if there’s a better place to do it that doesn’t require constant maintainence, ChangeBundleStateForPrimaryAssets seemed like the most central location to do it that doesn’t require ongoing maintainence.

The only other location I could see that might make sense are all the locations where FAssetBundleEntry::AssetPaths is added to…

  1. FAssetBundleData::AddBundleAsset
  2. FAssetBundleData::AddBundleAssets
  3. …etc…

Is this what you are suggesting?

Hello Thomas, I’m one of the programmers who wrote the CoreRedirects originally, and here’s how it works:

Whenever you load an asset, it first looks for the original name, it will try to follow any UObjectRedirector assets it finds, and then if that fails it applies CoreRedirects if it can’t find it. This code path definitely works for bundle assets in the editor, we often use it on our internal games. This code is spread throughout LinkerLoad and is a bit hard to understand. So it works 99% of the time, but is not working for your situation. The reason FixupRedirectedAssetPath exists is that sometimes you want to look up a redirected path WITHOUT actually loading the object. But this is significantly more expensive than handling redirects as part of trying to load it, which is why we don’t want to do it for every single asset load. But you can of course make that change in your own engine as a work around

From what you wrote above, it sounds like the issue is only happening with /Script/Atlas.InventoryComponent? From your stack it’s calling StaticLoadObject() on that path, which is a very unusual thing to do. The issue is that you can’t actually load native classes like /Script/Atlas.InventoryComponent with StaticLoadObject, because they’re either in memory or they aren’t. It’s still supposed to find the class if it is already in memory, and use core redirects as needed.

So the core bug is that for some reason trying to StaticLoadObject /Script/Atlas.InventoryComponent is not correctly applying CoreRedirects like it does for every other object. That’s why we were asking for more details, this DOES work for almost every type of object, just not in this specific case.

I’m looking through the code now to see if I can identify why this is failing randomly, but that is probably related to something going on in your game as part of the cook. Does the Atlas module still exist, but the component was moved? Is the CCPGameplayInventory module always loaded or is it a GameFeaturePlugin that is loaded dynamically? One possibility is that this works correctly for ASYNC loading but is not working correctly for the synchronous loading that happens depending on the specific part of the cook process this is happening in. Your stack shows it trying to do a synchronous load

Thanks [mention removed]​! Glad we got to the bottom of the root cause. Would you be able to reply here with the work around that you decide upon? I’d be keen to merge that in favour of my work around.

“In the meantime, you should be able to simplify your workaround by only calling FinalBundlePath.FixupCoreRedirects()” - Yeah, I thought about this, I ended up going for the full thing out of safety.

“Or, if you resave the original asset that had the issue in the editor it should go away as that also calls FixupCoreRedirects” - Yes, this is definitely a fix however, due to the lack of reporting it’s not trivial to know which asset needs resaving, if there was a clearer error message stating which asset contains the bad reference then we would probably just do that instead however, it’s not always possible to resave all affected assets when applying a redirector so, some kind of code solution is needed.

Is it the loop in FStreamableManager::RequestAsyncLoadInternal that isn’t doing the fixup before adding it to TargetSet?

for (const FSoftObjectPath& TargetName : NewRequest->RequestedAssets) { if (TargetName.IsNull()) { --NumValidRequests; continue; } else if (FPackageName::IsShortPackageName(TargetName.GetLongPackageFName())) { UE_LOG(LogStreamableManager, Error, TEXT("RequestAsyncLoad(%s) called with invalid package name %s"), *DebugName, *TargetName.ToString()); NewRequest->CancelHandle(); return nullptr; } TargetSet.Add(TargetName); }

The current fix we’re testing is to change the streamable manager’s ResolveRedirects to also handle CoreRedirects, as that is what was already handling the asset redirectors (which is why that part of the asset manager code is redundant). So if you want to test as well, can you try changing that function to be:

`FSoftObjectPath FStreamableManager::ResolveRedirects(const FSoftObjectPath& Target) const
{
FRedirectedPath const* Redir = StreamableRedirects.Find(Target);
if (Redir)
{
check(Target != Redir->NewPath);
UE_LOG(LogStreamableManager, Verbose, TEXT(“Redirected %s → %s”), *Target.ToString(), *Redir->NewPath.ToString());
return Redir->NewPath;
}

// If it wasn’t an asset redirect, try applying CoreRedirects
FSoftObjectPath ReturnPath = Target;
ReturnPath.FixupCoreRedirects();

return ReturnPath;
}`