dotnet/wpf · error · InvalidOperationException

SR.MeasureReentrancyInvalid

Error message

SR.MeasureReentrancyInvalid

What it means

TextBlock throws this InvalidOperationException when a public API or text-view callback is invoked while the layout Measure pass is still running. WPF requires layout passes to complete atomically; mutating content or querying layout during measure would corrupt cached line metrics. VerifyReentrancy() at the start of every public method guards against this.

Solutions

  1. Move the mutating call out of the measure path: defer it with Dispatcher.BeginInvoke(DispatcherPriority.Loaded/Background) or post it after the current layout pass completes.
  2. If inside MeasureOverride of a derived class, compute desired size without modifying the TextBlock's content; do content changes before calling base.MeasureOverride or in response to user input instead.
  3. Cache or batch property updates and apply them once after layout (e.g. on CompositionTarget.Rendering or via BeginInvoke) rather than per-measure-event.
  4. If triggered by an automation peer or textview callback, defer the query until the layout pass finishes instead of resolving it inline.

Example fix

// before: modifying text during layout
protected override Size MeasureOverride(Size constraint)
{
    if (needsUpdate) { textBlock.Text = ComputeText(); } // throws MeasureReentrancyInvalid
    return base.MeasureOverride(constraint);
}

// after: defer the mutation until the layout pass is over
protected override Size MeasureOverride(Size constraint)
{
    if (needsUpdate)
    {
        Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new Action(() => textBlock.Text = ComputeText()));
        needsUpdate = false;
    }
    return base.MeasureOverride(constraint);
}
Defensive patterns

Strategy: try-catch

Validate before calling

bool safeToTouchTextBlock = !textBlock.IsMeasureValid || !Dispatcher.HasStarted || Dispatcher.CheckAccess();
// schedule work only when not inside a layout pass:
if (!safeToTouchTextBlock) Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new Action(UpdateText));

Type guard

static bool CanModifyNow(TextBlock tb) => tb.Dispatcher.CheckAccess() && !DesignerProperties.GetIsInDesignMode(tb) && tb.IsLoaded == false || tb.IsMeasureValid && tb.IsArrangeValid;

Try / catch

try
{
    textBlock.Text = newText;
}
catch (InvalidOperationException ex) when (ex.Message.Contains("measure"))
{
    Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new Action(() => textBlock.Text = newText));
}

Prevention

When it happens

Trigger: Calling any public TextBlock member (e.g. setting Text/Inlines, calling ContentStart/ContentEnd text positions, or textview methods like GetLineText/GetLineIndexFromCharacterIndex) from within MeasureOverride, from a layout-time event, or from code invoked synchronously during the measure pass of the TextBlock.

Common situations: Subscribing to events that fire during layout (e.g. ScrollChanged, LayoutUpdated) and touching the TextBlock; custom controls deriving from TextBlock and modifying Inlines inside MeasureOverride; automation/UIA peers querying text properties during measure.

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/287eb0708de628d4. Report an issue: GitHub.

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/TextBlock.cs:3559

            _flags = value ? (_flags | flags) : (_flags & (~flags));
        }

        // ------------------------------------------------------------------
        // CheckFlags returns true if all of passed flags in the bitmask are set.
        // ------------------------------------------------------------------
        private bool CheckFlags(Flags flags)
        {
            return ((_flags & flags) == flags);
        }

        // ------------------------------------------------------------------
        // Ensures none of our public (or textview) methods can be called during measure/arrange/content change.
        // ------------------------------------------------------------------
        private void VerifyReentrancy()
        {
            if(CheckFlags(Flags.MeasureInProgress))
            {
                throw new InvalidOperationException(SR.MeasureReentrancyInvalid);
            }

            if(CheckFlags(Flags.ArrangeInProgress))
            {
                throw new InvalidOperationException(SR.ArrangeReentrancyInvalid);
            }

            if(CheckFlags(Flags.ContentChangeInProgress))
            {
                throw new InvalidOperationException(SR.TextContainerChangingReentrancyInvalid);
            }
        }

        /// <summary>
        /// Returns index of the line that starts at the given dcp. Returns -1 if
        /// no line or the line metrics collection starts at the given dcp
        /// </summary>
        /// <param name="dcpLine">

View on GitHub (pinned to 81131a70a4)