Context
This issue intentionally tracks a distinct user-visible manifestation of the tab-drag crash reported in #20512:
- The crash occurs while the mouse button is still held.
- The tab does not need to be dropped, detached, or merged into another window.
- Crossing from the tab strip into terminal content, or back out of the content, can immediately terminate the whole
WindowsTerminal.exe process.
- To the user, an ordinary reorder gesture or an accidental movement below the tab strip appears to destroy the entire window and all of its tabs.
The fully symbolized dump confirms this manifestation happens specifically while XAML is processing drag-enter/drag-leave boundary transitions. It has the same implementation-level root cause as #20512, but the reproduction and UX are different enough to track independently.
Environment
Windows Terminal package: 1.24.11911.0
WindowsTerminal.exe: 1.24.2607.10001
Windows: Windows 10 Pro x64, 10.0.19045
Windows.UI.Xaml.dll: 10.0.19041.6456
Process uptime: 00:55:23
Dump: 806 MB full user-mode dump
Steps to reproduce
This is timing-sensitive and is easier to reproduce when the terminal is processing sustained output:
- Open a Terminal window containing multiple tabs.
- Run a workload that produces continuous/high-volume terminal output.
- Click and hold a tab as if reordering or detaching it.
- Move the pointer down across the boundary between the tab strip and terminal content.
- Continue holding the mouse button; do not drop the tab.
- Moving into or back out of the terminal content can terminate the entire Terminal process.
Expected behavior
Crossing the terminal-content boundary during an internal tab drag should be harmless. Terminal should continue the tab reorder/tear-out gesture and preserve every tab, regardless of current output volume.
Actual behavior
The Terminal process exits immediately and without a user-facing error. Event Viewer records:
Faulting application: WindowsTerminal.exe 1.24.2607.10001
Faulting module: Windows.UI.Xaml.dll 10.0.19041.6456
Exception code: 0xc000027b
Fault offset: 0x0000000000481782
Symbolized failure evidence
The important distinction from the representative stack in #20512 is the outer XAML drag state:
Windows_UI_Xaml!CDragDropState::RaiseDragOverOrDropEvents+0xa5
Windows_UI_Xaml!CDragDropState::RaiseDragEnterEvents+0x1f6
Windows_UI_Xaml!CDragDropState::RaiseDragLeaveEvents+0x24d
Windows_UI_Xaml!CDragDropState::RaiseEvents+0x46
Windows_UI_Xaml!ContentRootInput::DragDropProcessor::ProcessWinRtDragDrop+0x16e
Windows_UI_Xaml!DragDrop_RaiseEvent+0x118
Windows_UI_Xaml!DirectUI::DropOperationTarget::RaiseDragDropEventActionAsync+0x206
Windows_UI_Xaml!DirectUI::RaiseDragDropEventAsyncOperation::OnStart+0xa4
Windows_UI_Xaml!DirectUI::DropOperationTarget::RaiseDragDropEventOperationAsync+0x6a
Windows_UI_Xaml!DirectUI::DropOperationTarget::OverAsync+0x22
DataExchange!DropTargetInternal::DragOver+0x7b
That boundary transition raises Terminal's content DragOver handler. The nested portion of the same thread is:
Microsoft_Terminal_Control!
winrt::Microsoft::Terminal::Control::implementation::
TermControl::_DragOverHandler+0x3b
Microsoft_Terminal_Control!
winrt::impl::consume_Windows_UI_Xaml_IDragEventArgs2<...>::DataView+0xaf
Windows_UI_Xaml!DirectUI::DragEventArgsGenerated::get_DataView+0x54
Windows_UI_Xaml!DirectUI::DragEventArgs::get_DataViewImpl+0x51
DataExchange!DropTargetInternal::DragInfo::get_Data+0x42
combase!ObjectStubless
combase!ObjectStublessClient
combase!CClientChannel::SendReceive
combase!ClassicSTAThreadSendReceive
combase!CSyncClientCall::SendReceive2
combase!ClassicSTAThreadWaitForCall
combase!CCliModalLoop::BlockFn
combase!CCliModalLoop::PeekRPCAndDDEMessage
combase!CCliModalLoop::MyPeekMessage
user32!PeekMessageW
win32u!NtUserPeekMessage
ntdll!KiUserCallbackDispatcherContinue
user32!DispatchClientMessage
CoreMessaging!Microsoft::CoreUI::Dispatch::UserAdapter_WindowProc
CoreMessaging!Microsoft::CoreUI::Dispatch::UserAdapter_DoWork
CoreMessaging!Microsoft::CoreUI::Dispatch::UserAdapter::OnUserDispatch
CoreMessaging!Microsoft::CoreUI::Dispatch::EventLoop::Callback_RunCoreLoop
CoreMessaging!Microsoft::CoreUI::Dispatch::TimeoutManager::Callback_OnDispatch
Windows_UI_Xaml!CXcpDispatcher::MessageTimerCallbackStatic
Windows_UI_Xaml!CDeferredInvoke::DispatchQueuedMessage
Windows_UI_Xaml!CXcpDispatcher::WindowProc
Windows_UI_Xaml!CXcpDispatcher::OnReentrancyProtectedWindowMessage
combase!RoFailFastWithErrorContextInternal2
KERNELBASE!RaiseFailFastException // HRESULT 0x8000ffff, E_UNEXPECTED
ntdll!RtlRaiseNoncontinuableException
Exception record:
ExceptionAddress: Windows_UI_Xaml!
CXcpDispatcher::OnReentrancyProtectedWindowMessage+0x1fdc5e
ExceptionCode: c000027b
Confirmed root cause and relationship to #20512
This is the same underlying reentrancy defect as #20512:
- The drag-enter/drag-leave transition routes
DragOver to TermControl.
TermControl::_DragOverHandler+0x3b calls e.DataView() to determine whether the payload contains files or text.
DataView() enters DataExchange!DropTargetInternal::DragInfo::get_Data, which makes a synchronous cross-apartment COM request.
- COM pumps the STA message queue while waiting.
- The nested message pump dispatches pending XAML work through
CDeferredInvoke::DispatchQueuedMessage.
- XAML re-enters
CXcpDispatcher::OnReentrancyProtectedWindowMessage while the original drag callback is still active.
- XAML deliberately fail-fasts with
E_UNEXPECTED; WER surfaces it as 0xc000027b at Windows.UI.Xaml.dll+0x481782.
The distinct UX manifestation is the trigger: this dump proves that merely crossing the content boundary, including drag-enter/drag-leave processing, is sufficient. The user does not have to hover for long or complete a drop.
High output appears to amplify the timing window by leaving more deferred XAML render/layout/scroll work available to the nested COM pump. It is not an out-of-memory or renderer failure; neither buffer nor AtlasEngine code appears in the crashing path.
Current main still evaluates e.DataView() up to four times in TermControl::_DragOverHandler. Internal tab drags already have application-owned state and private windowId/pid properties, and should not need to enter the terminal content's external file/text drop inspection path.
Proposal
Fix the shared implementation defect from #20512 while explicitly covering drag-boundary transitions:
- Track internal-tab-drag lifetime starting at
TabDragStarting and reliably clear it on completion, cancellation, and drop-outside paths.
- Suppress terminal-content drop handling for internal tab drags before
TermControl::_DragOverHandler calls e.DataView(); temporarily disabling AllowDrop on terminal controls during an internal drag is another possible implementation.
- Preserve tab-strip drag/drop handling so reordering, cross-window movement, and tear-out continue working.
- For external file/text drags, retrieve and cache
DataView() only once, and avoid synchronous payload inspection in high-frequency DragOver where possible.
Throttling is not a complete mitigation because the first getter call can fail. A try/catch around e.DataView() cannot reliably contain a XAML RaiseFailFastException raised from the nested dispatcher.
Acceptance criteria
- Add focused RED evidence that an internal tab crossing into/out of terminal content exercises the unsafe
DataView() path during RaiseDragEnterEvents/RaiseDragLeaveEvents; after the fix, the same reproduction turns GREEN and does not enter that getter.
- Repeatedly cross a dragged tab over the tab-strip/content boundary, without dropping it, while continuous output is rendering; Terminal must remain alive and produce no
0xc000027b / Windows.UI.Xaml.dll+0x481782 failure.
- Internal tab drags must not invoke external file/text payload inspection in
TermControl::_DragOverHandler.
- Same-window reorder, cross-window tab movement, tear-out, drag cancellation, and drop-outside behavior must continue working.
- External file and text drops into terminal content must continue working.
- The fix must cover drag-enter and drag-leave transitions, not only steady-state
DragOver or completed Drop.
Related issues
Dump availability and privacy
The 806 MB full user dump is available privately to maintainers. It should not be attached publicly because Terminal dumps can contain scrollback, commands, environment data, and other sensitive information.
Context
This issue intentionally tracks a distinct user-visible manifestation of the tab-drag crash reported in #20512:
WindowsTerminal.exeprocess.The fully symbolized dump confirms this manifestation happens specifically while XAML is processing drag-enter/drag-leave boundary transitions. It has the same implementation-level root cause as #20512, but the reproduction and UX are different enough to track independently.
Environment
Steps to reproduce
This is timing-sensitive and is easier to reproduce when the terminal is processing sustained output:
Expected behavior
Crossing the terminal-content boundary during an internal tab drag should be harmless. Terminal should continue the tab reorder/tear-out gesture and preserve every tab, regardless of current output volume.
Actual behavior
The Terminal process exits immediately and without a user-facing error. Event Viewer records:
Symbolized failure evidence
The important distinction from the representative stack in #20512 is the outer XAML drag state:
That boundary transition raises Terminal's content
DragOverhandler. The nested portion of the same thread is:Exception record:
Confirmed root cause and relationship to #20512
This is the same underlying reentrancy defect as #20512:
DragOvertoTermControl.TermControl::_DragOverHandler+0x3bcallse.DataView()to determine whether the payload contains files or text.DataView()entersDataExchange!DropTargetInternal::DragInfo::get_Data, which makes a synchronous cross-apartment COM request.CDeferredInvoke::DispatchQueuedMessage.CXcpDispatcher::OnReentrancyProtectedWindowMessagewhile the original drag callback is still active.E_UNEXPECTED; WER surfaces it as0xc000027batWindows.UI.Xaml.dll+0x481782.The distinct UX manifestation is the trigger: this dump proves that merely crossing the content boundary, including drag-enter/drag-leave processing, is sufficient. The user does not have to hover for long or complete a drop.
High output appears to amplify the timing window by leaving more deferred XAML render/layout/scroll work available to the nested COM pump. It is not an out-of-memory or renderer failure; neither buffer nor AtlasEngine code appears in the crashing path.
Current
mainstill evaluatese.DataView()up to four times inTermControl::_DragOverHandler. Internal tab drags already have application-owned state and privatewindowId/pidproperties, and should not need to enter the terminal content's external file/text drop inspection path.Proposal
Fix the shared implementation defect from #20512 while explicitly covering drag-boundary transitions:
TabDragStartingand reliably clear it on completion, cancellation, and drop-outside paths.TermControl::_DragOverHandlercallse.DataView(); temporarily disablingAllowDropon terminal controls during an internal drag is another possible implementation.DataView()only once, and avoid synchronous payload inspection in high-frequencyDragOverwhere possible.Throttling is not a complete mitigation because the first getter call can fail. A
try/catcharounde.DataView()cannot reliably contain a XAMLRaiseFailFastExceptionraised from the nested dispatcher.Acceptance criteria
DataView()path duringRaiseDragEnterEvents/RaiseDragLeaveEvents; after the fix, the same reproduction turns GREEN and does not enter that getter.0xc000027b/Windows.UI.Xaml.dll+0x481782failure.TermControl::_DragOverHandler.DragOveror completedDrop.Related issues
Dump availability and privacy
The 806 MB full user dump is available privately to maintainers. It should not be attached publicly because Terminal dumps can contain scrollback, commands, environment data, and other sensitive information.