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 Mouse

View on GitHub (pinned to 2e47b11478)

Solutions

  1. Call Stop() on the MouseHoldRepeaterImpl before setting Timeout, then call Start() again if needed.
  2. Reconfigure timeouts during mouse-up / release events, not during mouse-down / hold.
  3. If dynamic timeout adjustment is needed mid-hold, add a pause-resume mechanism rather than changing Timeout directly.
  4. 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

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

Related errors


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