{"record":{"id":"287eb0708de628d4","repo":"dotnet/wpf","slug":"sr-measurereentrancyinvalid","errorCode":null,"errorMessage":"SR.MeasureReentrancyInvalid","messagePattern":"SR\\.MeasureReentrancyInvalid","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/TextBlock.cs","lineNumber":3559,"sourceCode":"            _flags = value ? (_flags | flags) : (_flags & (~flags));\n        }\n\n        // ------------------------------------------------------------------\n        // CheckFlags returns true if all of passed flags in the bitmask are set.\n        // ------------------------------------------------------------------\n        private bool CheckFlags(Flags flags)\n        {\n            return ((_flags & flags) == flags);\n        }\n\n        // ------------------------------------------------------------------\n        // Ensures none of our public (or textview) methods can be called during measure/arrange/content change.\n        // ------------------------------------------------------------------\n        private void VerifyReentrancy()\n        {\n            if(CheckFlags(Flags.MeasureInProgress))\n            {\n                throw new InvalidOperationException(SR.MeasureReentrancyInvalid);\n            }\n\n            if(CheckFlags(Flags.ArrangeInProgress))\n            {\n                throw new InvalidOperationException(SR.ArrangeReentrancyInvalid);\n            }\n\n            if(CheckFlags(Flags.ContentChangeInProgress))\n            {\n                throw new InvalidOperationException(SR.TextContainerChangingReentrancyInvalid);\n            }\n        }\n\n        /// <summary>\n        /// Returns index of the line that starts at the given dcp. Returns -1 if\n        /// no line or the line metrics collection starts at the given dcp\n        /// </summary>\n        /// <param name=\"dcpLine\">","sourceCodeStart":3541,"sourceCodeEnd":3577,"githubUrl":"https://github.com/dotnet/wpf/blob/81131a70a4c573cd62748a5c36908fc4d662daa9/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/TextBlock.cs#L3541-L3577","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["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.","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.","Cache or batch property updates and apply them once after layout (e.g. on CompositionTarget.Rendering or via BeginInvoke) rather than per-measure-event.","If triggered by an automation peer or textview callback, defer the query until the layout pass finishes instead of resolving it inline."],"exampleFix":"// before: modifying text during layout\nprotected override Size MeasureOverride(Size constraint)\n{\n    if (needsUpdate) { textBlock.Text = ComputeText(); } // throws MeasureReentrancyInvalid\n    return base.MeasureOverride(constraint);\n}\n\n// after: defer the mutation until the layout pass is over\nprotected override Size MeasureOverride(Size constraint)\n{\n    if (needsUpdate)\n    {\n        Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new Action(() => textBlock.Text = ComputeText()));\n        needsUpdate = false;\n    }\n    return base.MeasureOverride(constraint);\n}","handlingStrategy":"try-catch","validationCode":"bool safeToTouchTextBlock = !textBlock.IsMeasureValid || !Dispatcher.HasStarted || Dispatcher.CheckAccess();\n// schedule work only when not inside a layout pass:\nif (!safeToTouchTextBlock) Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new Action(UpdateText));","typeGuard":"static bool CanModifyNow(TextBlock tb) => tb.Dispatcher.CheckAccess() && !DesignerProperties.GetIsInDesignMode(tb) && tb.IsLoaded == false || tb.IsMeasureValid && tb.IsArrangeValid;","tryCatchPattern":"try\n{\n    textBlock.Text = newText;\n}\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"measure\"))\n{\n    Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new Action(() => textBlock.Text = newText));\n}","preventionTips":["Never mutate TextBlock.Text/Inlines from MeasureOverride, ArrangeOverride, LayoutUpdated, or SizeChanged handlers.","Defer programmatic text changes with Dispatcher.BeginInvoke to a priority below Render.","In derived controls, treat the measure/arrange pass as read-only.","Batch automation-driven queries instead of resolving them inline during layout."],"tags":["wpf","layout","reentrancy","invalidoperation"],"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"}