{"record":{"id":"2f0a3ec5fce2cf9d","repo":"dotnet/wpf","slug":"sr-textbreakpointhasbeendisposed","errorCode":null,"errorMessage":"SR.TextBreakpointHasBeenDisposed","messagePattern":"SR\\.TextBreakpointHasBeenDisposed","errorType":"exception","errorClass":"ObjectDisposedException","httpStatus":null,"severity":"error","filePath":"src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/TextFormatting/FullTextBreakpoint.cs","lineNumber":217,"sourceCode":"                _penaltyResource = IntPtr.Zero;\n                _isDisposed = true;\n                GC.KeepAlive(this);\n            }\n        }\n\n        #region TextBreakpoint\n\n        /// <summary>\n        /// Client to acquire a state at the point where breakpoint is determined by line breaking process; \n        /// can be null when the line ends by the ending of the paragraph. Client may pass this\n        /// value back to TextFormatter as an input argument to TextFormatter.FormatParagraphBreakpoints when \n        /// formatting the next set of breakpoints within the same paragraph.\n        /// </summary>\n        public override TextLineBreak GetTextLineBreak()\n        {\n            if (_isDisposed)\n            {\n                throw new ObjectDisposedException(SR.TextBreakpointHasBeenDisposed);\n            }\n            return _metrics.GetTextLineBreak(_ploline);\n        }\n\n\n        /// <summary>\n        /// Client to get the handle of the internal factors that are used to determine penalty of this breakpoint.\n        /// </summary>\n        /// <remarks>\n        /// Calling this method means that the client will now manage the lifetime of this unmanaged resource themselves using unsafe penalty handler.\n        /// We would make a correspondent call to notify our unmanaged wrapper to release them from duty of managing this \n        /// resource. \n        /// </remarks>\n        internal override IntPtr GetTextPenaltyResource()\n        {\n            if (_isDisposed)\n            {\n                throw new ObjectDisposedException(SR.TextBreakpointHasBeenDisposed);","sourceCodeStart":199,"sourceCodeEnd":235,"githubUrl":"https://github.com/dotnet/wpf/blob/81131a70a4c573cd62748a5c36908fc4d662daa9/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/TextFormatting/FullTextBreakpoint.cs#L199-L235","documentation":"FullTextBreakpoint.GetTextLineBreak throws ObjectDisposedException with SR.TextBreakpointHasBeenDisposed when the breakpoint's backing TextLine has already been disposed. WPF's text formatter disposes internal line/breakpoint objects after paragraph layout completes or a new formatting pass invalidates them; accessing them afterwards is invalid.","triggerScenarios":"Calling GetTextLineBreak() on a TextBreakpoint stored from a previous line/paragraph after the owning TextLine was disposed (e.g. after calling Dispose on the line, or after the formatter reused/disposed it during custom TextSource GetTextRunForLine flows).","commonSituations":"Custom TextSource implementations caching TextLineBreak/TextLine objects beyond one layout pass; deferred use of a breakpoint in background formatting; accessing cached lines after the paragraph was re-formatted.","solutions":["Extract all needed data (line break, metrics) before disposing the TextLine; do not hold the breakpoint across disposal.","Cache extracted data (TextLineBreak values, metrics) instead of the live TextBreakpoint object.","Re-format the line via TextFormatter if you need its breakpoint after it was disposed.","Guard access with a disposed check and re-create the breakpoint when needed."],"exampleFix":"// before\nTextLineBreak brk;\nusing (line) { }\nbrk = breakpoint.GetTextLineBreak(); // ObjectDisposedException\n// after\nTextLineBreak brk;\nusing (line)\n{\n    brk = breakpoint.GetTextLineBreak(); // capture before disposal\n}","handlingStrategy":"validation","validationCode":"// capture data before disposal\nvar brk = line.GetTextLineBreak(); // while line is alive\n","typeGuard":"bool BreakpointUsable(TextBreakpoint bp) => bp is { } && !bp.IsDisposed; // if exposed; otherwise track your own flag","tryCatchPattern":"try { brk = breakpoint.GetTextLineBreak(); }\ncatch (ObjectDisposedException)\n{\n    line = formatter.FormatLine(...); // re-format\n    brk = line.GetTextLineBreak();\n}","preventionTips":["Extract TextLineBreak data inside the formatting scope.","Cache extracted data, not live TextLine/TextBreakpoint objects.","Do not dispose lines the formatter or breakpoints still reference.","Track a disposed flag on cached text objects."],"tags":["wpf","text-formatting","object-disposed","lifetime"],"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"}