tui-cs/Terminal.Gui · error · InvalidOperationException
Cannot change timeout while mouse is held down. Call Stop()
Error message
Cannot change timeout while mouse is held down. Call Stop() first.
What it means
This error is thrown by MouseHoldRepeaterImpl.Timeout setter when attempting to change the Timeout property while the mouse button is currently held down (_isDown is true). The MouseHoldRepeaterImpl implements mouse-hold-repeat behavior (like auto-scroll on button hold). Changing the timeout mid-repeat would cause timing inconsistencies, so the API requires calling Stop() first to release the mouse grab before reconfiguring.
Source
Thrown at Terminal.Gui/ViewBase/Mouse/MouseHoldRepeaterImpl.cs:61
private readonly View _mouseGrabView;
private readonly ITimedEvents? _timedEvents;
private readonly IMouseGrabHandler? _mouseGrabber;
private Timeout? _timeout;
private Timeout? _userTimeout;
private bool _isDown;
private object? _timeoutToken;
/// <inheritdoc/>
public Timeout? Timeout
{
get => _userTimeout;
set
{
if (_isDown)
{
throw new InvalidOperationException ("Cannot change timeout while mouse is held down. Call Stop() first.");
}
_userTimeout = value;
}
}
/// <summary>
/// The most recent mouse event arguments associated with the mouse held down action.
/// </summary>
private Mouse? _mouseEvent;
public void Start (Mouse mouse)
{
if (_isDown)
{
return;
}
_mouseEvent = new MouseView on GitHub (pinned to 2e47b11478)
Solutions
- Call Stop() on the MouseHoldRepeaterImpl before setting Timeout, then call Start() again if needed.
- Reconfigure timeouts during mouse-up / release events, not during mouse-down / hold.
- If dynamic timeout adjustment is needed mid-hold, add a pause-resume mechanism rather than changing Timeout directly.
- In test code, ensure proper Start/Stop sequencing before changing configuration.
Example fix
// before -- changing Timeout while mouse is held throws repeater.Start(mouseEvent); repeater.Timeout = newTimeout; // throws if mouse still down // after -- stop before reconfiguring repeater.Stop(); repeater.Timeout = newTimeout; repeater.Start(mouseEvent);
Defensive patterns
Strategy: validation
Validate before calling
// Check _isDown state before changing Timeout (if accessible) // Public API: call Stop() before reconfiguring repeater.Stop(); repeater.Timeout = newTimeout;
Try / catch
try { repeater.Timeout = newTimeout; }
catch (InvalidOperationException ex) when (ex.Message.Contains("mouse is held down"))
{ repeater.Stop(); repeater.Timeout = newTimeout; repeater.Start(mouse); } Prevention
- Always call Stop() before changing the Timeout property on MouseHoldRepeaterImpl.
- Reconfigure timeouts during mouse-up handlers, not during mouse-down/hold.
- Implement a pause-resume pattern if dynamic timeout adjustment is needed mid-hold.
- In test code, ensure proper Start/Stop sequencing before changing configuration.
When it happens
Trigger: Thrown at MouseHoldRepeaterImpl.cs:61 when the Timeout setter is invoked while _isDown is true. _isDown is set true in Start(Mouse) and set false in Stop(). This means the mouse button was pressed (Start called) but not yet released (Stop not called) when code attempts to change the repeat timeout.
Common situations: Attempting to dynamically adjust mouse-hold-repeat speed in response to user actions while the button is held, UI event handlers that reconfigure mouse behavior mid-interaction, race conditions where timeout reconfiguration and mouse events overlap, or test code that changes timeout without simulating mouse-up.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- The Viewport of an AdornmentView cannot be modified.
- AdornmentView can only render to their Parent or Parent's Su
- Adornment must be of type Border
- The ShadowStyle of MarginView cannot be set
- Target
AI-assisted analysis of tui-cs/Terminal.Gui@2e47b11478 (2026-08-13).
Data as JSON: /api/errors/df7e06824e6f63af.
Report an issue: GitHub.