dotnet/csharplang · error · ArgumentException

Index must not be negative.

Error message

Index must not be negative.

What it means

Thrown by the `System.Index` constructor (shown in the C# 8.0 ranges proposal, and shipped in the BCL) when the `value` argument is negative. `Index` stores either a forward offset or a from-end offset encoded via bitwise complement, so a negative `value` is not representable and is rejected up front. The implicit `int -> Index` conversion routes through this constructor, so a negative int passed where an Index is expected also throws.

Source

Thrown at proposals/csharp-8.0/ranges.cs:12

namespace System
{
    public readonly struct Index
    {
        private readonly int _value;

        public int Value => _value < 0 ? ~_value : _value;
        public bool FromEnd => _value < 0;

        public Index(int value, bool fromEnd)
        {
            if (value < 0) throw new ArgumentException("Index must not be negative.", nameof(value));

            _value = fromEnd ? ~value : value;
        }

        public static implicit operator Index(int value)
            => new Index(value, fromEnd: false);
    }

    public readonly struct Range
    {
        public Index Start { get; }
        public Index End { get; }

        private Range(Index start, Index end)
        {
            this.Start = start;
            this.End = end;
        }

View on GitHub (pinned to 05eb4800fc)

Solutions

  1. Guard the computed value before constructing the Index: `if (i < 0) throw/handle`.
  2. Use the from-end form for end-relative positions: write `^n` (or `new Index(n, fromEnd: true)`) instead of `length - n`, which avoids negative intermediates.
  3. Clamp the value to >= 0 when a non-negative fallback is acceptable.
  4. Replace -1 sentinel returns from upstream code with nullable or Option-style results before they reach Index construction.

Example fix

// before
Index idx = i - span.Length; // throws if i < span.Length

// after
Index idx = i >= 0 ? new Index(i, fromEnd: false)
                   : throw new ArgumentOutOfRangeException(nameof(i));
Defensive patterns

Strategy: validation

Validate before calling

static Index SafeIndex(int value, bool fromEnd = false)
{
    if (value < 0) throw new ArgumentOutOfRangeException(nameof(value), "Index must not be negative.");
    return new Index(value, fromEnd);
}

Type guard

static bool IsValidIndexValue(int value) => value >= 0;

Try / catch

try { Index idx = computed; }
catch (ArgumentOutOfRangeException ex) when (ex.ParamName == nameof(computed))
{
    // fall back to ^0 or recompute from the collection length
}

Prevention

When it happens

Trigger: `new Index(-1, fromEnd: false)`, implicit conversion of a negative int to `Index`, or handing a computed offset that subtracted past zero into any API taking `Index` (e.g. `array[^...]`, slicing helpers).

Common situations: Subtraction like `position - length` that goes negative; loop indices that underflow; converting a 'from end' count incorrectly; interop with code that returns -1 as a sentinel and feeding it straight into an Index-taking API.

Related errors


AI-assisted analysis of dotnet/csharplang@05eb4800fc (2026-08-13). Data as JSON: /api/errors/2cfac3753ed66b30. Report an issue: GitHub.