{"record":{"id":"5be9ccb5d16b488f","repo":"dotnet/wpf","slug":"sr-cachereqestcanonlypoptop","errorCode":null,"errorMessage":"SR.CacheReqestCanOnlyPopTop","messagePattern":"SR\\.CacheReqestCanOnlyPopTop","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Microsoft.DotNet.Wpf/src/UIAutomation/UIAutomationClient/System/Windows/Automation/CacheRequest.cs","lineNumber":198,"sourceCode":"        /// <summary>\n        /// Pop this CacheRequest from the current thread's stack of CacheRequests,\n        /// restoring the previously active CacheRequest.\n        /// </summary>\n        /// <remarks>\n        /// Only the currently active CacheRequest can be popped, attempting to pop\n        /// a CacheRequest which is not the current one will result in an InvalidOperation\n        /// Exception.\n        ///\n        /// The CacheRequest stack initially contains a default CacheRequest, which\n        /// cannot be popped off the stack.\n        /// </remarks>\n        public void Pop()\n        {\n            // ensure that this is top of stack\n            // (no lock needed here, since this is per-thread state)\n            if (_threadStack == null || _threadStack.Count == 0 || _threadStack.Peek() != this)\n            {\n                throw new InvalidOperationException(SR.CacheReqestCanOnlyPopTop);\n            }\n\n            _threadStack.Pop();\n\n            lock (_instanceLock)\n            {\n                _refCount--;\n            }\n        }\n\n        /// <summary>\n        /// Make this the currenly active CacheRequest.\n        /// </summary>\n        /// <remarks>\n        /// Returns an IDisposable which should be disposed\n        /// when finished using this CacheRequest to deactivate it.\n        /// This method is designed for use within a 'using' clause.\n        /// </remarks>","sourceCodeStart":180,"sourceCodeEnd":216,"githubUrl":"https://github.com/dotnet/wpf/blob/81131a70a4c573cd62748a5c36908fc4d662daa9/src/Microsoft.DotNet.Wpf/src/UIAutomation/UIAutomationClient/System/Windows/Automation/CacheRequest.cs#L180-L216","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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"],"exampleFix":"// before\nvar req = new CacheRequest();\nreq.Push();\nsomeInnerRequest.Push();\nreq.Pop(); // InvalidOperationException: not top of stack\n// after\nvar req = new CacheRequest();\nreq.Push();\nsomeInnerRequest.Push();\nsomeInnerRequest.Pop();\nreq.Pop(); // LIFO order","handlingStrategy":"validation","validationCode":"// Push/Pop only in matched LIFO pairs on the same thread\nif (ReferenceEquals(myThreadStack.LastOrDefault(), request)) request.Pop();","typeGuard":"bool CanPop(CacheRequest r) => r != null && !r.IsDefault; // track your own per-thread stack","tryCatchPattern":null,"preventionTips":["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"],"tags":["uiautomation","caching","invalid-operation","lifo-order"],"backgroundTag":"invalid-state-transition","analyzedSha":"81131a70a4c573cd62748a5c36908fc4d662daa9","analyzedAt":"2026-09-14T10:12:48.479Z","contentChangedAt":"2026-09-14T10:12:48.479Z","schemaVersion":2},"datasetVersion":"2026-09-21T21:30:21.729Z"}