dotnet/wpf · error · InvalidOperationException
SR.TextFormatterReentranceProhibited
Error message
SR.TextFormatterReentranceProhibited
What it means
AcquireContext throws InvalidOperationException(SR.TextFormatterReentranceProhibited) when the same TextFormatter instance is reentered during optimal break computation. A TextFormatter instance allows only one formatting context at a time; nesting optimal-break content requires a separate TextFormatter per nesting level.
Solutions
- Create a new TextFormatter instance for each nesting level of optimal break content
- Use TextFormatter.Create() inside your TextSource callbacks instead of the shared instance
- Restructure the TextSource so it does not reenter FormatLine during formatting
Example fix
// before
class MySource : TextSource {
TextFormatter _fmt = TextFormatter.Create(); // shared
public override TextRun GetTextRun(int i) { _fmt.FormatLine(...); } // reentrancy
}
// after
class MySource : TextSource {
public override TextRun GetTextRun(int i) {
using (var inner = TextFormatter.Create()) { /* format nested content */ }
}
} Defensive patterns
Strategy: validation
Validate before calling
if (_formattingInProgress && ReferenceEquals(currentFormatter, this)) throw new InvalidOperationException("Reentrancy detected; use a new TextFormatter per nesting level"); Try / catch
try { formatter.FormatLine(...); } catch (InvalidOperationException) { var nested = TextFormatter.Create(); /* retry with nested formatter */ } Prevention
- Create a separate TextFormatter instance for each nesting level
- Never share one TextFormatter across reentrant TextSource callbacks
When it happens
Trigger: Calling TextFormatter.FormatLine again (directly or via TextSource callbacks) on the same TextFormatter instance while an optimal-break formatting operation is already in progress.
Common situations: A TextSource implementation that itself calls into the same TextFormatter to build runs; recursive layout where the same shared TextFormatter singleton is used at multiple nesting levels of optimal-break content.
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.Automation_RecursivePublicCall
- SR.CannotModifyVisualChildrenDuringTreeWalk
- SR.DrawingGroup_AlreadyOpen
- SR.TextContainerChangingReentrancyInvalid
- Animation_Invalid_DefaultValue
AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14).
Data as JSON: /api/errors/367714a7e467a08c.
Report an issue: GitHub.
Appendix: source
Thrown at src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/TextFormatting/TextFormatterImp.cs:549
}
if (c == contextCount)
{
if (contextCount == 0 || !_multipleContextProhibited)
{
// no free one exists, create a new one
context = new TextFormatterContext();
_contextList.Add(context);
}
else
{
// This instance of TextFormatter only allows a single context, reentering the
// same TextFormatter in this case is not allowed.
//
// This requirement is currently enforced only during optimal break computation.
// Client implementing nesting of optimal break content inside another must create
// a separate TextFormatter instance for each content in different nesting level.
throw new InvalidOperationException(SR.TextFormatterReentranceProhibited);
}
}
Debug.Assert(context != null);
context.Owner = owner;
return context;
}
/// <summary>
/// Create an anti-inversion transform from the inversion flags.
/// The result is used to correct glyph bitmap on an output to
/// a drawing surface with the specified inversions applied on.
/// </summary>
internal static MatrixTransform CreateAntiInversionTransform(
InvertAxes inversion,
double paragraphWidth,View on GitHub (pinned to 81131a70a4)