JamesNK/Newtonsoft.Json · error · JsonException

Path returned multiple tokens.

Error message

Path returned multiple tokens.

What it means

Thrown by SelectToken(string path) (the single-value overload) when the JSONPath expression matches more than one token. SelectToken is documented to return exactly one JToken; because the iterator over JPath.Evaluate yields a second element, the method rejects the ambiguous query with a JsonException rather than silently returning one of the matches. Use SelectTokens (plural) when a wildcard or recursive descent legitimately matches several nodes.

Source

Thrown at Src/Newtonsoft.Json/Linq/JToken.cs:2433

        /// <summary>
        /// Selects a <see cref="JToken"/> using a JSONPath expression. Selects the token that matches the object path.
        /// </summary>
        /// <param name="path">
        /// A <see cref="String"/> that contains a JSONPath expression.
        /// </param>
        /// <param name="settings">The <see cref="JsonSelectSettings"/> used to select tokens.</param>
        /// <returns>A <see cref="JToken"/>.</returns>
        public JToken? SelectToken(string path, JsonSelectSettings? settings)
        {
            JPath p = new JPath(path);

            JToken? token = null;
            foreach (JToken t in p.Evaluate(this, this, settings))
            {
                if (token != null)
                {
                    throw new JsonException("Path returned multiple tokens.");
                }

                token = t;
            }

            return token;
        }

        /// <summary>
        /// Selects a collection of elements using a JSONPath expression.
        /// </summary>
        /// <param name="path">
        /// A <see cref="String"/> that contains a JSONPath expression.
        /// </param>
        /// <returns>An <see cref="IEnumerable{T}"/> of <see cref="JToken"/> that contains the selected elements.</returns>
        public IEnumerable<JToken> SelectTokens(string path)
        {
            return SelectTokens(path, settings: null);

View on GitHub (pinned to 4f73e74372)

Solutions

  1. Switch to SelectTokens(path) (plural) when multiple matches are valid, and iterate over the results.
  2. Tighten the JSONPath to target a single node, e.g. replace $..name with $.user.name or $.items[0].id.
  3. Wrap SelectToken in try/catch JsonException when the query is user-supplied and cardinality is uncertain.

Example fix

// before
var name = doc.SelectToken("$..name");

// after
var names = doc.SelectTokens("$..name").ToList();
var name = names.Count == 1 ? names[0] : null;
Defensive patterns

Strategy: validation

Validate before calling

var matches = doc.SelectTokens(path).ToList();
if (matches.Count > 1) { /* use all, or tighten path */ }

Type guard

static bool IsSingleMatch(JToken root, string path) => root.SelectTokens(path).Take(2).Count() == 1;

Try / catch

try { var t = doc.SelectToken(path); } catch (JsonException) { /* switch to SelectTokens */ }

Prevention

When it happens

Trigger: Calling token.SelectToken("$..name") (recursive descent hitting multiple objects), token.SelectToken("$.items[*].id") (array wildcard), or any path containing '..', '[*]', or '[?(@...)]' filters on a document where more than one node satisfies the expression.

Common situations: A path that worked on a sample with a single element breaks once the array grows. Switching from a fixed property access ($.user.name) to a wildcard query without changing to SelectTokens. Reusing a path across heterogeneous documents where cardinality is not guaranteed.

Related errors


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