JamesNK/Newtonsoft.Json · error · InvalidOperationException

Wrapped ICollection<T> does not support indexer.

Error message

Wrapped ICollection<T> does not support indexer.

What it means

CollectionWrapper<T> internally adapts either an IList or an ICollection<T> into one IList view used during (de)serialization. When it was constructed from a plain ICollection<T> (e.g. HashSet<T>, or any collection that implements only ICollection<T> and not IList), positional access is impossible because ICollection<T> has no indexer. The getter of the IList.this[int] accessor throws InvalidOperationException to fail loudly rather than silently returning wrong data (the _genericCollection branch at CollectionWrapper.cs:261-263).

Source

Thrown at Src/Newtonsoft.Json/Utilities/CollectionWrapper.cs:263

                }
            }
        }

        void IList.Remove(object? value)
        {
            if (IsCompatibleObject(value))
            {
                Remove((T)value!);
            }
        }

        object? IList.this[int index]
        {
            get
            {
                if (_genericCollection != null)
                {
                    throw new InvalidOperationException("Wrapped ICollection<T> does not support indexer.");
                }

                return _list![index];
            }
            set
            {
                if (_genericCollection != null)
                {
                    throw new InvalidOperationException("Wrapped ICollection<T> does not support indexer.");
                }

                VerifyValueType(value);
                _list![index] = (T?)value;
            }
        }

        void ICollection.CopyTo(Array array, int arrayIndex)
        {

View on GitHub (pinned to 4f73e74372)

Solutions

  1. Use an orderable collection type (List<T>, IList<T>, or T[]) for the property being (de)serialized instead of HashSet/ISet.
  2. If you need set semantics, deserialize into a List<T> first, then build the set from it.
  3. Avoid custom IContractResolver/converters that downcast collections to IList and call indexers on them.
  4. Audit any registered third-party JsonConverter that touches collections by index and replace it with one that enumerates.
  5. Expose the member as IList-backed so the wrapper takes the _list branch instead of the _genericCollection branch.

Example fix

// before
public HashSet<string> Tags { get; set; } = new(); // ICollection<T> only, no indexer
// after
public List<string> Tags { get; set; } = new();   // IList-backed, indexer works
Defensive patterns

Strategy: validation

Validate before calling

// Before any positional access on a wrapped collection, confirm it is indexable.
IList? indexable = collection as IList;
if (indexable == null) {
    // enumerate instead of indexing; do NOT call wrapper[i]
    foreach (var item in (System.Collections.IEnumerable)collection) { /* ... */ }
}

Type guard

static bool HasIndexer<T>(T collection) => collection is System.Collections.IList;

Try / catch

try { var x = wrapper[i]; } catch (InvalidOperationException) when (wrapped is ICollection<string>) { /* fall back to enumeration */ }

Prevention

When it happens

Trigger: Code (internal Newtonsoft pipeline, a custom IContractResolver/IConverter, or a caller that downcasts to IList) invokes wrapper[index] on a CollectionWrapper whose _list is null and _genericCollection is set — i.e. the wrapped object is a pure ICollection<T>, not also an IList.

Common situations: Serializing/deserializing types whose collection members are sets (HashSet<T>/ISet<T>) or custom collections implementing only ICollection<T>; custom contracts that force IList-based positional reads; third-party converters that assume positional access.

Related errors


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