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
- Use an orderable collection type (List<T>, IList<T>, or T[]) for the property being (de)serialized instead of HashSet/ISet.
- If you need set semantics, deserialize into a List<T> first, then build the set from it.
- Avoid custom IContractResolver/converters that downcast collections to IList and call indexers on them.
- Audit any registered third-party JsonConverter that touches collections by index and replace it with one that enumerates.
- 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
- Prefer List<T>/T[] for JSON collection properties so the wrapper takes the IList branch.
- Never assume ICollection<T> supports positional access.
- If you need a set, deserialize to List<T> then construct the set.
- Audit custom converters/contract resolvers that touch collections by index.
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
- The value '{0}' is not of type '{1}' and cannot be used in t
- Current error context error is different to requested error.
- Could not clear error context. Error context is already null
- Could not cast or convert from {0} to {1}.
- Could not get constructor for {0}.
AI-assisted analysis of JamesNK/Newtonsoft.Json@4f73e74372 (2026-08-07).
Data as JSON: /api/errors/f2a67482d2ae13af.
Report an issue: GitHub.