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
- 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).
- Gate wrap toggles on user input only, outside the model's own change notifications.
- 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
- Never mutate WordWrap from within TextView event handlers or commands.
- Defer wrap changes to after the current operation completes.
- Gate programmatic wrap toggles on user input, not on internal change notifications.
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.