AvaloniaUI/Avalonia · error · InvalidOperationException

Stack was empty.

Error message

Stack was empty.

What it means

InvalidOperationException "Stack was empty." thrown by PooledStack<T>.ThrowForEmptyStack(), called from Peek() and Pop() when the stack has zero elements (`(uint)(size-1) >= (uint)array.Length` is the bounds-safe empty check). It signals an attempt to read/remove from a drained stack rather than returning default. TryPeek/TryPop exist as the non-throwing alternatives.

Source

Thrown at src/Avalonia.Base/Collections/Pooled/PooledStack.cs:574

        public T[] ToArray()
        {
            if (_size == 0)
                return Array.Empty<T>();

            T[] objArray = new T[_size];
            int i = 0;
            while (i < _size)
            {
                objArray[i] = _array[_size - i - 1];
                i++;
            }
            return objArray;
        }

        private void ThrowForEmptyStack()
        {
            Debug.Assert(_size == 0);
            throw new InvalidOperationException("Stack was empty.");
        }

        private void ReturnArray(T[]? replaceWith = null)
        {
            if (_array?.Length > 0)
            {
                try
                {
                    _pool.Return(_array, clearArray: _clearOnFree);
                }
                catch (ArgumentException)
                {
                    // oh well, the array pool didn't like our array
                }
            }

            if (!(replaceWith is null))
            {

View on GitHub (pinned to 11c5427268)

Solutions

  1. Use TryPop/TryPeek which return false instead of throwing.
  2. Guard with `if (stack.Count > 0)` before Peek/Pop.
  3. Bound the drain loop with `while (stack.Count > 0)`.

Example fix

// before
while (true)
    DoWork(stack.Pop()); // throws when drained

// after
while (stack.TryPop(out var item))
    DoWork(item);
Defensive patterns

Strategy: validation

Validate before calling

if (!stack.TryPop(out var item))
    return; // stack empty, handle gracefully
DoWork(item);

Prevention

When it happens

Trigger: Calling `stack.Peek()` or `stack.Pop()` on an empty PooledStack<T>; popping in a loop past the last element; consuming a stack that another code path already drained.

Common situations: A drain loop like `while (true) stack.Pop();` without a Count check; shared/multi-step processing where an earlier step emptied the stack; assuming a queue/stack is non-empty after a filter.

Related errors


AI-assisted analysis of AvaloniaUI/Avalonia@11c5427268 (2026-08-13). Data as JSON: /api/errors/7a2d1b09e31b994a. Report an issue: GitHub.