'Malformed sequence hierarchy' ensure hit after modifying sub-sequence tracks

Looking at the callstack, this issue stems from `MovieSceneHelpers::GetSingleBoundObject`. In this function, a sequence ID is found by calling `EvaluationState->FindSequenceId(Sequence)`. There are two issues with this:

  1. It’s possible for there to be multiple instances of the same sequence in the evaluation states cache if the same sequence is used in multiple sub-sections.
  2. Caches are never removed from the evaluation state so the sequence ID that it returns may be invalid (ie: not in the sequence hierarchy)!

The second issue is what’s happening in the repro provided: when we remove the sub-section and readd a new sub-section with the same sequence, the sequence IDs for the sections are not the same. This means we end up with two separate cache entries in the evaluation state, one of which is no longer valid. Anytime from this point on (until sequencer is closed and reopened) any calls to `FMovieSceneSequenceIDRef::FindSequenceId` will return a sequence ID that no longer exists in the sequence hierarchy, and if we end up in a code path where this invalid ID makes its way to `FSubSequencePath::Reset` we will hit an ensure.

Ideally, I think we would:

  1. Remove stale sequences from `FMovieSceneEvaluationState::ObjectCaches` (ie: when a sub-section is deleted) so that `FMovieSceneSequenceIDRef::FindSequenceId` will never return an invalid sequence ID.
  2. Avoid calling `FMovieSceneSequenceIDRef::FindSequenceId` wherever possible since it’s not guaranteed that there is only one sequence ID per sequence.

Steps to Reproduce

  1. Create a new level sequence named RootSequence
  2. Add RootSequence to an empty level
  3. Open RootSequence in Sequencer
  4. Add a spawnable static mesh Cube to RootSequence
  5. Add a subsequence track to RootSequence
  6. Create a new subsequence in the subsequence track named SubSequence (click + > Create new Subsequence Track Asset)
  7. In SubSequence, create a possessable binding to the spawnable in the root sequence (select Cube in the Outliner and select Add > Add Actor to Track > Add ‘Cube’ in Sequencer)
  8. In RootSequence, remove the subsequence section containing SubSequence
  9. In RootSequence, add a new subsequence section containing SubSequence
  10. In SubSequence, add a new static mesh component binding for Cube (on Cube possessable binding click + StaticMeshComponent0)

At this point the ensure will be hit.

Ensure

Script Stack (0 frames) :

Ensure condition failed: CurrentNode && OuterSubData [File:Engine\Source\Runtime\MovieScene\Private\Evaluation\MovieSceneRootOverridePath.cpp] [Line: 81]

Malformed sequence hierarchy

[2025.09.05-00.09.26:484][775]LogOutputDevice: Error: Ensure condition failed: CurrentNode && OuterSubData [File:Engine\Source\Runtime\MovieScene\Private\Evaluation\MovieSceneRootOverridePath.cpp] [Line: 81]

Malformed sequence hierarchy

Callstack

 	UnrealEditor-MovieScene.dll!UE::MovieScene::FSubSequencePath::Reset::__l19::<lambda_1>::operator()() Line 81	C++
>	UnrealEditor-MovieScene.dll!UE::MovieScene::FSubSequencePath::Reset(FMovieSceneSequenceID LeafID, const FMovieSceneSequenceHierarchy * RootHierarchy) Line 81	C++
 	UnrealEditor-MovieScene.dll!UE::MovieScene::FSubSequencePath::FSubSequencePath(FMovieSceneSequenceID LeafID, const FMovieSceneSequenceHierarchy * RootHierarchy) Line 30	C++
 	UnrealEditor-MovieScene.dll!UE::MovieScene::ResolveExternalSequenceID(FMovieSceneSequenceID SourceSequenceID, int RemapSourceParentIndex, FMovieSceneSequenceID TargetSequenceID, TSharedRef<UE::MovieScene::FSharedPlaybackState const ,1> SharedPlaybackState) Line 38	C++
 	UnrealEditor-MovieScene.dll!FMovieSceneObjectBindingID::ResolveToFixed(FMovieSceneSequenceID RuntimeSequenceID, TSharedRef<UE::MovieScene::FSharedPlaybackState const ,1> SharedPlaybackState) Line 186	C++
 	UnrealEditor-MovieScene.dll!FMovieSceneObjectCache::UpdateBindings(const FGuid & InGuid, TSharedRef<UE::MovieScene::FSharedPlaybackState const ,1> SharedPlaybackState) Line 485	C++
 	UnrealEditor-MovieScene.dll!FMovieSceneObjectCache::FindBoundObjects(const FGuid & InBindingID, TSharedRef<UE::MovieScene::FSharedPlaybackState const ,1> InSharedPlaybackState) Line 75	C++
 	UnrealEditor-MovieScene.dll!FMovieSceneEvaluationState::FindBoundObjects(const FGuid & ObjectBindingID, const FMovieSceneSequenceID & SequenceID, TSharedRef<UE::MovieScene::FSharedPlaybackState const ,1> SharedPlaybackState) Line 425	C++
 	UnrealEditor-MovieScene.dll!MovieSceneHelpers::GetResolutionContext(UMovieSceneSequence * Sequence, const FGuid & ObjectId, const FMovieSceneSequenceID & SequenceID, TSharedRef<UE::MovieScene::FSharedPlaybackState const ,1> SharedPlaybackState) Line 1096	C++
 	UnrealEditor-MovieScene.dll!MovieSceneHelpers::GetSingleBoundObject(UMovieSceneSequence * Sequence, const FGuid & ObjectId, TSharedRef<UE::MovieScene::FSharedPlaybackState const ,1> SharedPlaybackState, int BindingIndex) Line 862	C++
 	UnrealEditor-LevelSequenceEditor.dll!FMovieSceneBindingPropertyInfoListCustomization::CustomizeDetails(IDetailLayoutBuilder & DetailBuilder) Line 219	C++
 	UnrealEditor-PropertyEditor.dll!DetailLayoutHelpers::QueryCustomDetailLayout(FDetailLayoutData & LayoutData, const TMap<TWeakObjectPtr<UStruct,FWeakObjectPtr>,FDetailLayoutCallback,FDefaultSetAllocator,TDefaultMapHashableKeyFuncs<TWeakObjectPtr<UStruct,FWeakObjectPtr>,FDetailLayoutCallback,0>> & InstancedDetailLayoutMap, const TDelegate<TSharedRef<IDetailCustomization,1> __cdecl(void),FDefaultDelegateUserPolicy> & GenericLayoutDelegate) Line 358	C++
 	UnrealEditor-PropertyEditor.dll!SDetailsViewBase::UpdateSinglePropertyMap(TSharedPtr<FComplexPropertyNode,1> InRootPropertyNode, FDetailLayoutData & LayoutData, bool bIsExternal) Line 725	C++
 	UnrealEditor-PropertyEditor.dll!SDetailsViewBase::UpdatePropertyMaps() Line 636	C++
 	UnrealEditor-PropertyEditor.dll!SDetailsViewBase::UpdatePropertyMapsWithItemExpansions() Line 688	C++
 	UnrealEditor-PropertyEditor.dll!SDetailsView::PostSetObject(const TArray<FDetailsViewObjectRoot,TSizedDefaultAllocator<32>> & Roots) Line 1069	C++
 	UnrealEditor-PropertyEditor.dll!SDetailsView::SetObjectArrayPrivate(const TArray<UObject *,TSizedDefaultAllocator<32>> & InObjects) Line 834	C++
 	UnrealEditor-PropertyEditor.dll!SDetailsView::SetObjects(const TArray<UObject *,TSizedDefaultAllocator<32>> & InObjects, bool bForceRefresh, bool bOverrideLock) Line 585	C++
 	UnrealEditor-PropertyEditor.dll!SDetailsView::SetObject(UObject * InObject, bool bForceRefresh) Line 612	C++
 	UnrealEditor-LevelSequenceEditor.dll!ULevelSequenceEditorSubsystem::RefreshBindingDetails(IDetailsView * DetailsView, FGuid ObjectBindingID) Line 3282	C++
 	UnrealEditor-LevelSequenceEditor.dll!ULevelSequenceEditorSubsystem::AddBindingPropertiesMenu(FMenuBuilder & MenuBuilder) Line 2702	C++
 	[Inline Frame] UnrealEditor-LevelSequenceEditor.dll!ULevelSequenceEditorSubsystem::AddBindingPropertiesSidebar(FMenuBuilder &) Line 2757	C++
 	UnrealEditor-LevelSequenceEditor.dll!ULevelSequenceEditorSubsystem::Initialize::__l2::<lambda_10>::operator()(FMenuBuilder & MenuBuilder) Line 724	C++
 	[External Code]	
 	[Inline Frame] UnrealEditor-Slate.dll!TDelegate<void __cdecl(FMenuBuilder &),FDefaultDelegateUserPolicy>::ExecuteIfBound(FMenuBuilder &) Line 635	C++
 	UnrealEditor-Slate.dll!FExtender::Apply(FName ExtensionHook, EExtensionHook::Position HookPosition, FMenuBuilder & MenuBuilder) Line 189	C++
 	UnrealEditor-Slate.dll!FMenuBuilder::ApplyHook(FName InExtensionHook, EExtensionHook::Position HookPosition) Line 530	C++
 	UnrealEditor-Slate.dll!FMenuBuilder::BeginSection(FName InExtensionHook, const TAttribute<FText> & InHeadingText, const TAttribute<EVisibility> & InVisibility, const TAttribute<FMenuEntryResizeParams> & InResizeParams) Line 344	C++
 	UnrealEditor-Sequencer.dll!FSequencerSelectionDrawer::BuildOutlinerDetails(const TSharedRef<UE::Sequencer::FSequencerSelection,1> & InSelection, FMenuBuilder & MenuBuilder) Line 386	C++
 	UnrealEditor-Sequencer.dll!FSequencerSelectionDrawer::UpdateFromSelectionNextFrame() Line 274	C++
 	UnrealEditor-Engine.dll!FTimerUnifiedDelegate::Execute() Line 367	C++
 	UnrealEditor-Engine.dll!FTimerManager::Tick(float DeltaTime) Line 1076	C++
 	UnrealEditor-UnrealEd.dll!UEditorEngine::Tick(float DeltaSeconds, bool bIdleMode) Line 1806	C++
 	UnrealEditor-UnrealEd.dll!UUnrealEdEngine::Tick(float DeltaSeconds, bool bIdleMode) Line 537	C++
 	UnrealEditor.exe!FEngineLoop::Tick() Line 5878	C++
 	[Inline Frame] UnrealEditor.exe!EngineTick() Line 63	C++
 	UnrealEditor.exe!GuardedMain(const wchar_t * CmdLine) Line 198	C++
 	UnrealEditor.exe!LaunchWindowsStartup(HINSTANCE__ * hInInstance, HINSTANCE__ * hPrevInstance, char * __formal, int nCmdShow, const wchar_t * CmdLine) Line 288	C++
 	UnrealEditor.exe!WinMain(HINSTANCE__ * hInInstance, HINSTANCE__ * hPrevInstance, char * pCmdLine, int nCmdShow) Line 368	C++

Thanks for raising this. I’ve logged an issue that you can follow at https://issues.unrealengine.com/issue/UE-318757.

Talking with the dev team, not calling FindSequenceId would be the issue in this case and is on their radar.

Thanks,

Dustin