tui-cs/Terminal.Gui · error · InvalidOperationException

WordWrap settings was changed after the {_currentCaller} cal

Error message

WordWrap settings was changed after the {_currentCaller} call.

What it means

Thrown by TextView's word-wrap machinery (InvalidOperationException, interpolated message) when _currentCaller is non-null at a point where the wrap routine expected it to be cleared — i.e. word-wrap settings were changed in the middle of an operation tracked by _currentCaller. The wrap pass sets _currentCaller to detect exactly this: if a callee mutates WordWrap while a dependent operation is in flight, the invariants the operation relied on are broken, so it aborts.

Source

Thrown at Terminal.Gui/Views/TextInput/TextView/TextView.WordWrap.cs:387

                                       out int nStartCol,
                                       CurrentRow,
                                       CurrentColumn,
                                       _selectionStartRow,
                                       _selectionStartColumn,
                                       _tabWidth,
                                       true);
            CurrentRow = nRow;
            CurrentColumn = nCol;
            _selectionStartRow = nStartRow;
            _selectionStartColumn = nStartCol;
            _wrapNeeded = true;

            SetNeedsDraw ();
        }

        if (_currentCaller is { })
        {
            throw new InvalidOperationException ($"WordWrap settings was changed after the {_currentCaller} call.");
        }
    }

    /// <summary>
    ///     INTERNAL: Wraps or rewraps the text model to fit the current Viewport width.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         This method regenerates the wrapped model whenever the viewport width changes or when
    ///         word wrap is first enabled. It is typically called during layout operations.
    ///     </para>
    ///     <para>
    ///         The wrapping process:
    ///         <list type="bullet">
    ///             <item>Takes each line from the original model</item>
    ///             <item>Breaks it into multiple lines to fit within the viewport width</item>
    ///             <item>Preserves tab expansion based on <see cref="TabWidth"/></item>
    ///             <item>Maintains word boundaries (doesn't break words mid-character)</item>

View on GitHub (pinned to 2e47b11478)

Solutions

  1. Do not mutate WordWrap from within TextView event handlers or commands; defer the change to after the operation completes (e.g. via Application.Invoke/Post).
  2. Gate wrap toggles on user input only, outside the model's own change notifications.
  3. If a programmatic wrap change is required mid-session, queue it and apply it once _currentCaller has cleared.

Example fix

// before
textView.TextChanged += (_, _) => { textView.WordWrap = !textView.WordWrap; }; // throws 177

// after
bool pending = false;
textView.TextChanged += (_, _) => pending = true;
// apply later, outside the operation:
app.Invoke(() => { if (pending) { textView.WordWrap = !textView.WordWrap; pending = false; } });
Defensive patterns

Strategy: validation

Validate before calling

// Do not change WordWrap inside TextView handlers; defer
textView.TextChanged += (_, _) => { _pendingWrapToggle = true; };
app.Invoke(() => { if (_pendingWrapToggle) { textView.WordWrap = !textView.WordWrap; _pendingWrapToggle = false; } });

Prevention

When it happens

Trigger: Toggling WordWrap (or a setting that forces a rewrap) from within a callback/handler invoked during a TextView operation that set _currentCaller — e.g. changing wrap inside a TextChanged, selection, or layout handler that runs mid-operation.

Common situations: A reactive handler that re-enables/disables word wrap in response to content events; programmatic wrap toggles triggered from within command handling; recursive layout driven by a wrap change.

Related errors


AI-assisted analysis of tui-cs/Terminal.Gui@2e47b11478 (2026-08-13). Data as JSON: /api/errors/57ea51e7c170e2fc. Report an issue: GitHub.