{"record":{"id":"2cfac3753ed66b30","repo":"dotnet/csharplang","slug":"index-must-not-be-negative","errorCode":null,"errorMessage":"Index must not be negative.","messagePattern":"Index must not be negative\\.","errorType":"exception","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"proposals/csharp-8.0/ranges.cs","lineNumber":12,"sourceCode":"namespace System\n{\n    public readonly struct Index\n    {\n        private readonly int _value;\n\n        public int Value => _value < 0 ? ~_value : _value;\n        public bool FromEnd => _value < 0;\n\n        public Index(int value, bool fromEnd)\n        {\n            if (value < 0) throw new ArgumentException(\"Index must not be negative.\", nameof(value));\n\n            _value = fromEnd ? ~value : value;\n        }\n\n        public static implicit operator Index(int value)\n            => new Index(value, fromEnd: false);\n    }\n\n    public readonly struct Range\n    {\n        public Index Start { get; }\n        public Index End { get; }\n\n        private Range(Index start, Index end)\n        {\n            this.Start = start;\n            this.End = end;\n        }","sourceCodeStart":1,"sourceCodeEnd":30,"githubUrl":"https://github.com/dotnet/csharplang/blob/05eb4800fc3dc76259ccd49ac01f1eeb49222380/proposals/csharp-8.0/ranges.cs#L1-L30","documentation":"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.","triggerScenarios":"`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).","commonSituations":"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.","solutions":["Guard the computed value before constructing the Index: `if (i < 0) throw/handle`.","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.","Clamp the value to >= 0 when a non-negative fallback is acceptable.","Replace -1 sentinel returns from upstream code with nullable or Option-style results before they reach Index construction."],"exampleFix":"// before\nIndex idx = i - span.Length; // throws if i < span.Length\n\n// after\nIndex idx = i >= 0 ? new Index(i, fromEnd: false)\n                   : throw new ArgumentOutOfRangeException(nameof(i));","handlingStrategy":"validation","validationCode":"static Index SafeIndex(int value, bool fromEnd = false)\n{\n    if (value < 0) throw new ArgumentOutOfRangeException(nameof(value), \"Index must not be negative.\");\n    return new Index(value, fromEnd);\n}","typeGuard":"static bool IsValidIndexValue(int value) => value >= 0;","tryCatchPattern":"try { Index idx = computed; }\ncatch (ArgumentOutOfRangeException ex) when (ex.ParamName == nameof(computed))\n{\n    // fall back to ^0 or recompute from the collection length\n}","preventionTips":["Prefer the `^n` from-end syntax for end-relative positions to avoid negative intermediates.","Guard any subtraction that feeds an Index: `if (i < 0) ...`.","Never pipe -1 sentinel returns directly into Index-taking APIs; convert them first."],"tags":["index","range","argument-validation","csharp"],"backgroundTag":null,"analyzedSha":"05eb4800fc3dc76259ccd49ac01f1eeb49222380","analyzedAt":"2026-08-13T17:44:03.908Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}