dotnet/wpf · error · InvalidOperationException
SR.CacheReqestCanOnlyPopTop
Error message
SR.CacheReqestCanOnlyPopTop
What it means
CacheRequest.Pop() removes a CacheRequest pushed onto the current thread's cache-request stack. The framework throws InvalidOperationException because the request being popped is not the top of the thread's stack (or the stack is empty / the request was never pushed on this thread). Only the most recently pushed request on a given thread can be popped.
Solutions
- Ensure every Push() has exactly one matching Pop() on the same thread, in LIFO order
- Use CacheRequest.Activate() with a using/dispose pattern so Pop happens even on exceptions
- Do not share CacheRequest instances across threads; create one per thread
- Check the stack order: pop the innermost (last pushed) request first
Example fix
// before var req = new CacheRequest(); req.Push(); someInnerRequest.Push(); req.Pop(); // InvalidOperationException: not top of stack // after var req = new CacheRequest(); req.Push(); someInnerRequest.Push(); someInnerRequest.Pop(); req.Pop(); // LIFO order
Defensive patterns
Strategy: validation
Validate before calling
// Push/Pop only in matched LIFO pairs on the same thread if (ReferenceEquals(myThreadStack.LastOrDefault(), request)) request.Pop();
Type guard
bool CanPop(CacheRequest r) => r != null && !r.IsDefault; // track your own per-thread stack
Prevention
- Prefer CacheRequest.Activate() with using over manual Push/Pop
- Keep Push/Pop in the same method/scope
- Never share CacheRequest instances across threads
- Pop innermost requests first
When it happens
Trigger: Calling Pop() on a CacheRequest that was never Push()ed on the current thread; calling Pop() twice for one Push(); popping an outer request while an inner request is still on the stack; pushing on one thread and popping on another (the stack is per-thread).
Common situations: Unbalanced Push/Pop pairs around automation element reads; exceptions thrown inside the push scope skipping the Pop; cross-thread sharing of a CacheRequest instance; using CacheRequest.Activate/ActivateNoRewind with manual Pop instead of the RAII-style AutomationElement.CachedChildren patterns.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- SR.CacheReqestCantModifyWhileActive
- Operation cannot be performed.
- Selection container does not support multiple selection.
- SR.CantPrefetchTextPattern
- SR.OperationCannotBePerformed
AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14).
Data as JSON: /api/errors/5be9ccb5d16b488f.
Report an issue: GitHub.
Appendix: source
Thrown at src/Microsoft.DotNet.Wpf/src/UIAutomation/UIAutomationClient/System/Windows/Automation/CacheRequest.cs:198
/// <summary>
/// Pop this CacheRequest from the current thread's stack of CacheRequests,
/// restoring the previously active CacheRequest.
/// </summary>
/// <remarks>
/// Only the currently active CacheRequest can be popped, attempting to pop
/// a CacheRequest which is not the current one will result in an InvalidOperation
/// Exception.
///
/// The CacheRequest stack initially contains a default CacheRequest, which
/// cannot be popped off the stack.
/// </remarks>
public void Pop()
{
// ensure that this is top of stack
// (no lock needed here, since this is per-thread state)
if (_threadStack == null || _threadStack.Count == 0 || _threadStack.Peek() != this)
{
throw new InvalidOperationException(SR.CacheReqestCanOnlyPopTop);
}
_threadStack.Pop();
lock (_instanceLock)
{
_refCount--;
}
}
/// <summary>
/// Make this the currenly active CacheRequest.
/// </summary>
/// <remarks>
/// Returns an IDisposable which should be disposed
/// when finished using this CacheRequest to deactivate it.
/// This method is designed for use within a 'using' clause.
/// </remarks>View on GitHub (pinned to 81131a70a4)