Исключение ArgumentOutOfRangeException с текстом «Длина не может быть меньше нуля. Имя параметра: length» возникает в .NET-приложениях, когда в метод Substring или аналогичную строковую операцию передаётся отрицательное значение длины. Чаще всего виновник — выражение вида str.Substring(0, str.IndexOf(ch)), где символ-разделитель в строке не найден, и IndexOf возвращает -1.

Ошибка проявляется внезапно: код может месяцами работать на «правильных» данных и упасть на первой же строке, где ожидаемый разделитель отсутствует. Ниже разберём, как локализовать проблемное место, какие сценарии приводят к отрицательной длине и как исправить код без костылей.

Что означает это исключение

Сообщение «Length cannot be less than zero. Parameter name: length» — это локализованный текст исключения ArgumentOutOfRangeException, которое .NET выбрасывает при передаче недопустимого аргумента. Имя параметра length указывает, что отрицательным оказался именно аргумент длины, а не стартовый индекс.

Типичные методы, где возникает эта ошибка:

  • 🔧 String.Substring(startIndex, length) — самый частый источник;
  • 📏 String.Remove(startIndex, count) при отрицательном count;
  • ✂️ Конструкторы и методы работы с массивами, принимающие длину сегмента;
  • 📂 Пользовательские методы, внутри которых вызывается одна из перечисленных операций.

Обратите внимание: исключение выбрасывается в момент вызова метода, поэтому в трассировке стека всегда видно точную строку кода, где произошёл сбой.

Классический сценарий: Substring вместе с IndexOf

Самая распространённая причина — комбинация IndexOf и Substring без проверки результата поиска. Метод IndexOf возвращает -1, если подстрока не найдена, и это значение напрямую попадает в параметр длины.

string line = "ключ=значение";

int pos = line.IndexOf('=');

string key = line.Substring(0, pos); // упадёт, если '=' нет

Если в line нет символа =, переменная pos станет равна -1, и вызов Substring(0, -1) выбросит именно то исключение, которое вы видите. Такое случается при разборе конфигурационных файлов, CSV-строк, ответов внешних сервисов и пользовательского ввода — везде, где формат данных не гарантирован.

💡

Ошибка «длина не может быть меньше нуля» почти всегда означает, что IndexOf или LastIndexOf вернул -1, а результат не был проверен перед передачей в Substring.

Как найти проблемное место в коде

Первым делом откройте трассировку стека (stack trace) — она содержит имя метода и номер строки, где выброшено исключение. Если приложение логирует ошибки, ищите запись с типом System.ArgumentOutOfRangeException.

Полезный порядок действий:

  • 🔍 Найдите в стеке верхний фрейм, относящийся к вашему коду, а не к библиотекам .NET;
  • 🧭 Проверьте все вызовы Substring, Remove и IndexOf в этом методе;
  • 🧪 Поставьте точку останова и посмотрите фактическое значение строки и вычисленной длины;
  • 📋 Сохраните входные данные, на которых падает код, — они понадобятся для проверки исправления.
⚠️ Внимание: если ошибка возникает только у части пользователей или периодически, проблема почти наверняка во входных данных, а не в логике. Не спешите переписывать алгоритм — сначала зафиксируйте реальную строку, вызвавшую сбой.
📊 Где вы столкнулись с этой ошибкой?
При разборе строк (Substring/IndexOf)
При чтении файла конфигурации
В чужом коде или сторонней библиотеке
Пока не могу определить источник

Способы исправления

Правильное исправление — не подавление исключения через try-catch, а проверка индекса до вызова строковой операции. Минимальный безопасный вариант выглядит так:

int pos = line.IndexOf('=');

if (pos >= 0)

{

string key = line.Substring(0, pos);

}

Альтернативы в зависимости от задачи:

  • ✂️ line.Split('=') — не падает при отсутствии разделителя, просто возвращает массив из одного элемента;
  • 🛡️ Проверка line.Contains('=') перед извлечением части строки;
  • 🧩 Регулярные выражения с явной проверкой Match.Success для сложных форматов;
  • 📐 Метод-обёртка, который возвращает пустую строку или значение по умолчанию при некорректном входе.

☑️ Чек-лист исправления ошибки

Выполнено: 0 / 5

Ошибка в чужом коде или библиотеке

Иногда исключение прилетает из сторонней библиотеки, формата файла или внутренностей фреймворка. В этом случае виноваты по-прежнему данные: библиотека пытается разобрать строку, которая не соответствует ожидаемому формату.

Что можно сделать без изменения чужого кода: проверьте, какие значения вы передаёте в библиотеку — пути к файлам, строки подключения, параметры конфигурации. Пустые значения, отсутствующие ключи в app.config / appsettings.json и неожиданные символы в путях — частые косвенные причины.

Пример

ошибка при чтении конфигурации:Если приложение читает строку вида "Server=host;Port=5432" и разбирает её через Substring по индексу '=', то отсутствие одного из параметров (например, строка обрезана в файле) приведёт к IndexOf = -1 и падению с ошибкой про length. Проверьте целостность конфигурационного файла и отсутствие пустых секций.

Профилактика: как не допустить повторения

Чтобы ошибка не возвращалась, встройте защиту на уровне архитектуры. Валидация входных данных на границе системы — файлы, сетевые ответы, пользовательский ввод — дешевле, чем отладка исключений в глубине логики.

💡

Включите в проекте анализ nullable reference types (C# 8+) и обрабатывайте пустые строки явно — значительная часть подобных сбоев связана с null или String.Empty там, где ожидались данные.

Сравнение подходов к разбору строк:

ПодходПадает при отсутствии разделителяКогда применять
Substring + IndexOf без проверкиДаТолько при гарантированном формате
Substring + IndexOf с проверкой на -1НетКогда нужна часть строки до разделителя
SplitНетПростое разделение на части
Regex с Match.SuccessНетСложные или переменные форматы
⚠️ Внимание: оборачивание падающего кода в пустой try-catch скрывает ошибку, но не решает её — приложение продолжит работать с некорректными данными, что может привести к более серьёзным сбоям дальше по логике.

Часто задаваемые вопросы

Почему ошибка появляется только на некоторых данных?

Потому что исключение выбрасывается лишь тогда, когда вычисленная длина становится отрицательной — например, когда разделитель в конкретной строке отсутствует. На «правильных» данных тот же код работает без сбоев.

Может ли ошибка возникать не из-за Substring?

Да. Любой метод, принимающий параметр длины — Remove, методы работы с массивами и сегментами, сторонние библиотеки — может выбросить это исключение. Смотрите трассировку стека, чтобы определить точный источник.

Что делать, если stack trace указывает на системную библиотеку?

Поднимитесь по стеку выше — до первого фрейма вашего кода. Почти всегда системный метод лишь реагирует на некорректный аргумент, переданный из приложения.

Поможет ли замена Substring на диапазоны C# (str[..pos])?

Нет, оператор диапазона также выбросит исключение при отрицательном или выходящем за границы индексе. Проверка результата IndexOf обязательна в любом случае.

Как предотвратить такие ошибки в командной разработке?

Помогают статический анализ кода, юнит-тесты на граничные случаи (пустая строка, строка без разделителя, строка из одного разделителя) и код-ревью с акцентом на обработку результатов IndexOf.