dotnet/wpf · error · ObjectDisposedException

SR.TextBreakpointHasBeenDisposed

Error message

SR.TextBreakpointHasBeenDisposed

What it means

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.

Solutions

  1. Extract all needed data (line break, metrics) before disposing the TextLine; do not hold the breakpoint across disposal.
  2. Cache extracted data (TextLineBreak values, metrics) instead of the live TextBreakpoint object.
  3. Re-format the line via TextFormatter if you need its breakpoint after it was disposed.
  4. Guard access with a disposed check and re-create the breakpoint when needed.

Example fix

// before
TextLineBreak brk;
using (line) { }
brk = breakpoint.GetTextLineBreak(); // ObjectDisposedException
// after
TextLineBreak brk;
using (line)
{
    brk = breakpoint.GetTextLineBreak(); // capture before disposal
}
Defensive patterns

Strategy: validation

Validate before calling

// capture data before disposal
var brk = line.GetTextLineBreak(); // while line is alive

Type guard

bool BreakpointUsable(TextBreakpoint bp) => bp is { } && !bp.IsDisposed; // if exposed; otherwise track your own flag

Try / catch

try { brk = breakpoint.GetTextLineBreak(); }
catch (ObjectDisposedException)
{
    line = formatter.FormatLine(...); // re-format
    brk = line.GetTextLineBreak();
}

Prevention

When it happens

Trigger: 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).

Common situations: 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.

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/2f0a3ec5fce2cf9d. Report an issue: GitHub.

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/TextFormatting/FullTextBreakpoint.cs:217

                _penaltyResource = IntPtr.Zero;
                _isDisposed = true;
                GC.KeepAlive(this);
            }
        }

        #region TextBreakpoint

        /// <summary>
        /// Client to acquire a state at the point where breakpoint is determined by line breaking process; 
        /// can be null when the line ends by the ending of the paragraph. Client may pass this
        /// value back to TextFormatter as an input argument to TextFormatter.FormatParagraphBreakpoints when 
        /// formatting the next set of breakpoints within the same paragraph.
        /// </summary>
        public override TextLineBreak GetTextLineBreak()
        {
            if (_isDisposed)
            {
                throw new ObjectDisposedException(SR.TextBreakpointHasBeenDisposed);
            }
            return _metrics.GetTextLineBreak(_ploline);
        }


        /// <summary>
        /// Client to get the handle of the internal factors that are used to determine penalty of this breakpoint.
        /// </summary>
        /// <remarks>
        /// Calling this method means that the client will now manage the lifetime of this unmanaged resource themselves using unsafe penalty handler.
        /// We would make a correspondent call to notify our unmanaged wrapper to release them from duty of managing this 
        /// resource. 
        /// </remarks>
        internal override IntPtr GetTextPenaltyResource()
        {
            if (_isDisposed)
            {
                throw new ObjectDisposedException(SR.TextBreakpointHasBeenDisposed);

View on GitHub (pinned to 81131a70a4)