Skip to content

fix(functions): report the resolved key in sort_by type errors - #392

Open
simpleqt wants to merge 1 commit into
jmespath:developfrom
simpleqt:fix/sort-by-error-reports-resolved-key
Open

simpleqt wants to merge 1 commit into
jmespath:developfrom
simpleqt:fix/sort-by-error-reports-resolved-key

Conversation

@simpleqt

@simpleqt simpleqt commented Nov 3, 2026

Copy link
Copy Markdown

Fixes the value reported by JMESPathTypeError when sort_by rejects the type of a sort key.

Root cause

jmespath/functions.py, _func_sort_by (jmespath/functions.py:319-323 on develop):

required_type = self._convert_to_jmespath_type(
    type(expref.visit(expref.expression, array[0])).__name__)
if required_type not in ['number', 'string']:
    raise exceptions.JMESPathTypeError(
        'sort_by', array[0], required_type, ['string', 'number'])

The type is computed from the resolved key, but the error is constructed with array[0], the array element the key was resolved against. _func_min_by and _func_max_by, which take the identical expression->number|expression->string argument, construct the error with the key (jmespath/functions.py:331-347 + _create_key_func), so the three functions disagree.

Reproduction

>>> import jmespath
>>> jmespath.search('sort_by(@, &k)', [{'k': [1, 2]}])

Before (on develop, 2812594):

JMESPathTypeError: In function sort_by(), invalid type for value: {'k': [1, 2]},
  expected one of: ['string', 'number'], received: "array"

The message is self-contradictory: it claims to have received an "array" while printing an object. Note also that the element is not what the constraint applies to — the specification's signature is

sort_by(array elements, expression->number|expression->string expr)

so the constrained value is what expr resolves to. min_by/max_by on the same input report value: [1], i.e. the resolved key.

After:

JMESPathTypeError: In function sort_by(), invalid type for value: [1, 2],
  expected one of: ['string', 'number'], received: "array"

Fix

The type check now goes through the same key function that performs the sort, so the key is evaluated and converted exactly as min_by/max_by do it, and the reported value is the key. The key is resolved once for the type check and once per comparison during the sort, which is the same number of evaluations as before (the old code also evaluated array[0] twice for the first element).

Tests

tests/test_functions.py::TestFunctions::test_sort_by_reports_the_resolved_key_in_type_errors

RED on develop (2812594):

$ python -m pytest tests/test_functions.py -q -k sort_by_reports
E   AssertionError: 'invalid type for value: [1, 2]' not found in
    'In function sort_by(), invalid type for value: {\'k\': [1, 2]},
     expected one of: [\'string\', \'number\'], received: "array"'
1 failed in 0.32s

GREEN with the fix:

$ python -m pytest tests/test_functions.py -q
6 passed in 0.20s

$ python -m pytest tests -q
992 passed, 1 skipped in 1.05s

The new test sits next to the existing test_type_error_messages, which asserts the same message shape for length().

Impact

Callers that catch JMESPathTypeError and surface its message (or use the message to tell their users which value was rejected) are told that an element of the input array has an invalid type, when in fact it is the key resolved from that element. The error type was already correct, and no successful result changes; a before/after sweep over empty arrays, single elements, ties, float keys, string keys, mixed number/string keys, sort_by(@, &a.b), and sort_by(x, &to_number(k)) produced identical results and identical error types, differing only in the value printed for the first-element key error.

`sort_by` validates the type of the sort key, but when the key resolves to
something other than a number or a string it raised JMESPathTypeError with
the *array element* as the offending value:

    >>> jmespath.search('sort_by(@, &k)', [{'k': [1, 2]}])
    JMESPathTypeError: In function sort_by(), invalid type for value:
    {'k': [1, 2]}, expected one of: ['string', 'number'], received: "array"

The signature in the specification is

    sort_by(array elements, expression->number|expression->string expr)

so the value the constraint applies to is the value `expr` resolved to,
not the element it was resolved against.  The reported value was also
inconsistent with the reported type: the message claimed to have received
an "array" while showing an object.  `min_by` and `max_by`, which share the
same signature, already report the resolved key; this makes `sort_by`
consistent with them:

    >>> jmespath.search('sort_by(@, &k)', [{'k': [1, 2]}])
    JMESPathTypeError: In function sort_by(), invalid type for value: [1, 2],
    expected one of: ['string', 'number'], received: "array"

The type check now goes through the same key function that performs the
sort, so the key is resolved exactly once for the type check and once per
sort comparison, as before.  Error types, error values for keys that are
already valid, and all successful results are unchanged.

Signed-off-by: simpleqt <2015900818@qq.com>
Copilot AI balanced review requested due to automatic review settings November 3, 2026 22:29

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota. Try requesting a review at a lower effort level.

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.

2 participants