JamesNK/Newtonsoft.Json · error · ArgumentOutOfRangeException

length

Error message

length

What it means

Defensive guard inside the internal helper StringUtils.Trim(string s, int start, int length) (Src/Newtonsoft.Json/Utilities/StringUtils.cs:334). It throws ArgumentOutOfRangeException with parameter name "length" whenever length is negative (StringUtils.cs:346-348), because a negative window size for trimming the substring [start, start+length) is meaningless. The only in-library caller, ReflectionUtils.SplitFullyQualifiedTypeName (ReflectionUtils.cs:867-868), derives length from a validated comma-delimiter index and can never produce a negative value, so this guard is effectively unreachable from any public Newtonsoft.Json deserialization/serialization path.

Source

Thrown at Src/Newtonsoft.Json/Utilities/StringUtils.cs:348

        {
            return (source.Length > 0 && source[source.Length - 1] == value);
        }

        public static string Trim(this string s, int start, int length)
        {
            // References: https://referencesource.microsoft.com/#mscorlib/system/string.cs,2691
            // https://referencesource.microsoft.com/#mscorlib/system/string.cs,1226
            if (s == null)
            {
                throw new ArgumentNullException();
            }
            if (start < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(start));
            }
            if (length < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(length));
            }
            int end = start + length - 1;
            if (end >= s.Length)
            {
                throw new ArgumentOutOfRangeException(nameof(length));
            }
            for (; start < end; start++)
            {
                if (!char.IsWhiteSpace(s[start]))
                {
                    break;
                }
            }
            for (; end >= start; end--)
            {
                if (!char.IsWhiteSpace(s[end]))
                {
                    break;

View on GitHub (pinned to 4f73e74372)

Solutions

  1. Guard or clamp length before calling: use int safeLength = Math.Max(0, length); so a negative computed value collapses to an empty window.
  2. Recompute length defensively from boundaries (end - start) instead of passing a derived value that can underflow.
  3. Prefer System.String.Trim() / Substring(start, len) for application code and reserve this internal overload for library internals.

Example fix

// before
string t = s.Trim(start, end - start); // underflows when end < start

// after
string t = (end >= start) ? s.Trim(start, end - start) : s;
Defensive patterns

Strategy: validation

Validate before calling

int safeLength = Math.Max(0, length);
if (start < 0) start = 0;
string result = s.Trim(start, safeLength);

Prevention

When it happens

Trigger: Directly invoking the StringUtils.Trim(string, int, int) extension overload with a negative length argument (e.g. s.Trim(0, -1)). It is NOT reachable through the public API: SplitFullyQualifiedTypeName computes length as either assemblyDelimiterIndex (>=0) or fullyQualifiedTypeName.Length - assemblyDelimiterIndex - 1 (>=0 since the delimiter index is always <= Length-1 from GetAssemblyDelimiterIndex), so the guard only fires for code that calls this utility directly or in a fork.

Common situations: Contributors/fork maintainers calling the windowed Trim overload with hand-computed indices; unit tests exercising StringUtils directly; custom type-name splitters that reuse this helper with off-by-one arithmetic that underflows below zero.

Related errors


AI-assisted analysis of JamesNK/Newtonsoft.Json@4f73e74372 (2026-08-07). Data as JSON: /api/errors/552c160f8cdabbb5. Report an issue: GitHub.