Skip to content

Match nothing instead of raising KeyError for Index on a dict (fixes #93)#234

Open
gaoflow wants to merge 1 commit into
h2non:masterfrom
gaoflow:fix-index-find-dict-keyerror
Open

Match nothing instead of raising KeyError for Index on a dict (fixes #93)#234
gaoflow wants to merge 1 commit into
h2non:masterfrom
gaoflow:fix-index-find-dict-keyerror

Conversation

@gaoflow

@gaoflow gaoflow commented Jun 20, 2026

Copy link
Copy Markdown

Summary

Fixes #93. Index.find (the concrete [n] syntax) raises an uncaught KeyError when it is applied to a dict:

import jsonpath_ng
jsonpath_ng.parse("$.*[0].b").find({"a": [{"b": 1}], "c": {"d": 2}})
# KeyError: 0

The wildcard * matches every value (including the dict {"d": 2}), then [0] is applied to each — and indexing the dict raises. Minimal repro: jsonpath_ng.parse("[0]").find({"foo": 1}).

This contradicts the class's own docstring — "If the datum is None or not long enough, it will not crash but will not match anything" — and is inconsistent with the sibling paths: Slice ([*]) and Fields (.foo) both handle a dict datum gracefully. Only Index crashes.

Cause

In Index._find_base, the guard

if datum.value and len(datum.value) > index:
    rv += [DatumInContext(datum.value[index], ...)]

assumes anything with a len() is integer-indexable. A dict satisfies len(datum.value) > index, but datum.value[index] is then a key lookup that raises KeyError.

Fix

Return early when the datum is a dict — integer indices apply to sequences, not mappings, so (per the docstring) match nothing rather than crashing. Sequence and string indexing are untouched.

rv = []
if isinstance(datum.value, dict):
    return rv
for index in self.indices:
    ...

The fix is scoped to the find contract; find_or_create on an empty dict still converts it to a list first (in the create block above), so that path is unaffected.

Verification

  • Added two cases to the find_test_cases table: ("[0]", {"foo": 1}, [], []) and the issue Expression with wildcard then index selection errors with KeyError #93 repro ("$.*[0].b", {"a": [{"b": 1}], "c": {"d": 2}}, [1], ["((a.[0]).b)"]). They fail with KeyError before the fix and pass after (confirmed via git stash).
  • List/tuple/string indexing behavior is preserved.
  • Full suite: 355 passed (was 351).
  • Added a ### Fixed entry to CHANGELOG.md under [Unreleased].

This pull request was prepared with the assistance of AI, under my direction and review.

Index.find (concrete `[n]`) raised an uncaught KeyError when applied to
a dict, e.g. `$.*[0]` where the wildcard matched a dict value. The guard
`len(datum.value) > index` is satisfied by a dict, but `datum.value[index]`
is then a key lookup that raises. The sibling Slice and Fields paths
handle dicts gracefully, and the class docstring already promises it
'will not crash but will not match anything'.

Return early when the datum is a dict so integer indexing matches
nothing. Sequence and string indexing are unchanged.

Fixes h2non#93
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Expression with wildcard then index selection errors with KeyError

1 participant