dotnet/wpf · error · COMException
SR.TextStore_ReentrantRequestLock
Error message
SR.TextStore_ReentrantRequestLock
What it means
Thrown when a text service makes a reentrant RequestLock call while a lock is already held and the requested lock cannot be granted synchronously. WPF only tolerates reentrancy for async read requests that can be deferred (returning TS_S_ASYNC); any reentrant request involving write access, or a synchronous (TS_LF_SYNC) request during an active lock, raises this COMException.
Solutions
- Restructure the text service so it does not call RequestLock reentrantly from within OnLockGranted; queue the work and issue a new top-level RequestLock after the current one completes.
- If the work is read-only, request the lock asynchronously and handle the TS_S_ASYNC session result.
- Avoid TS_LF_SYNC lock requests while another lock is active.
Example fix
// before: reentrant request inside lock callback
void OnLockGranted() { store.RequestLock(LockFlags.TS_LF_WRITE, out hrSession); }
// after: defer
void OnLockGranted() { Dispatcher.BeginInvoke(() => store.RequestLock(LockFlags.TS_LF_WRITE, out hrSession)); } Defensive patterns
Strategy: validation
Validate before calling
bool insideLockCallback = true; // known context
if (insideLockCallback && ((flags & LockFlags.TS_LF_WRITE) != 0 || (flags & LockFlags.TS_LF_SYNC) != 0)) { /* defer the request instead */ } Try / catch
try { store.RequestLock(flags, out hrSession); } catch (COMException ex) { /* queue deferred request */ } Prevention
- Never call RequestLock from within OnLockGranted; defer via Dispatcher
- Use async read locks (TS_S_ASYNC) where possible
- Avoid TS_LF_SYNC requests while locks are active
When it happens
Trigger: Inside a lock-granted callback (e.g. OnLockGranted), the text service calls RequestLock again with TS_LF_WRITE, or with TS_LF_SYNC, or the pending request requires a write while the current lock is write-held and cannot be queued.
Common situations: IME composition logic that mutates text inside a lock callback instead of deferring work; nested layout/input reentrancy during IME replay of changes.
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
AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14).
Data as JSON: /api/errors/eedd7bc9cca83486.
Report an issue: GitHub.
Appendix: source
Thrown at src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Documents/TextStore.cs:154
"paf:", _pendingAsyncLockFlags);
if (_replayingIMEChangeReentrancyCount != 0)
{
IMECompositionTracer.Mark(new System.Diagnostics.StackTrace(true));
}
}
if (_lockFlags != 0)
{
// Normally, we disallow reentrant lock requests.
// However, there is one legal case. If the caller already
// holds a read lock, and is asking for a write lock, then
// we will grant that asynchronously as soon as they walk
// back up the stack to the original RequestLock call.
if (((_lockFlags & UnsafeNativeMethods.LockFlags.TS_LF_WRITE) == UnsafeNativeMethods.LockFlags.TS_LF_WRITE) ||
((flags & UnsafeNativeMethods.LockFlags.TS_LF_WRITE) == 0) ||
((flags & UnsafeNativeMethods.LockFlags.TS_LF_SYNC) == UnsafeNativeMethods.LockFlags.TS_LF_SYNC))
{
throw new COMException(SR.TextStore_ReentrantRequestLock);
}
if (!isReplayingIMEChanges)
{
_pendingWriteReq = true;
hrSession = UnsafeNativeMethods.TS_S_ASYNC;
}
else
{
// the outer request must also have been made while replaying
// IME changes. We can't grant the write-lock until the
// replay is done. So use the defer mechanism, rather than
// the _pendingWriteReq;
hrSession = DeferLockRequest(flags);
}
}
else
{View on GitHub (pinned to 81131a70a4)