Skip to content

fix(cli): Explain Explore aggregates rejected for their argument - #1927

Merged
BYK merged 7 commits into
mainfrom
fix/cli-explore-attribute-type-errors
Nov 4, 2026
Merged

BYK merged 7 commits into
mainfrom
fix/cli-explore-attribute-type-errors

Conversation

@sentry-junior

@sentry-junior sentry-junior Bot commented Nov 4, 2026 •

Copy link
Copy Markdown
Contributor

Part of CLI-10 ("API request failed: 400"). When sentry explore sends a user --field/--sort aggregate that Sentry rejects because of its argument, the CLI shows a raw 400 and reports it as a CLI bug. These are input mistakes: a string attribute in a numeric aggregate, transaction.duration on --dataset transactions (which queries spans), a non-numeric errors column, or an invalid count_if argument.

The CLI now reads the structured fields that getsentry/sentry#129954 (merged) adds to these 400s, "code": "invalid_function_argument" plus function and argument, instead of matching message text. ApiError keeps them next to code. When they name the same function and argument as a user expression that reached the request, toExploreFieldError (#1661) raises a ValidationError that keeps the server detail and adds a hint. Rejections of default or generated expressions, and 400s without the code (servers before that deploy), stay ApiErrors. The #1661 hints are unchanged.

Builds on #1936 for ApiError.code. Merge that first.

Before

$ sentry explore my-org/ --dataset spans -F 'p75(cart.total)'
Error: API request failed: 400 Bad Request
  Endpoint: /organizations/my-org/events/
  cart.total is invalid for parameter 1 in p75. Its a string type field, but it must be one of these types: {'number'}
# exit 30
$ sentry explore my-org/ --dataset transactions -F 'sum(transaction.duration)'
Error: API request failed: 400 Bad Request
  Endpoint: /organizations/my-org/events/
  transaction.duration is invalid for parameter 1 in sum. Its a string type field, but it must be one of these types: {'number', 'duration'}
# exit 30

After

With the server sending code, function and argument:

$ sentry explore my-org/ --dataset spans -F 'p75(cart.total)'
Error: Sentry rejected --field "p75(cart.total)" for dataset "spans".

cart.total is invalid for parameter 1 in p75. Its a string type field, but it must be one of these types: {'number'}

Try:
  p75() does not accept cart.total on dataset "spans": it may not exist there, or may not have the type p75() needs. Reference a numeric custom attribute with its type, e.g. tags[cart.total,number]; to check the key and its type, run: sentry attribute list <org>/ cart.total --dataset spans
# exit 21
$ sentry explore my-org/ --dataset transactions -F 'sum(transaction.duration)'
Error: Sentry rejected --field "sum(transaction.duration)" for dataset "spans".

transaction.duration is invalid for parameter 1 in sum. Its a string type field, but it must be one of these types: {'number', 'duration'}

Try:
  Use span.duration instead of transaction.duration. Transactions are queried as spans, which store their duration in span.duration.
# exit 21
$ sentry explore my-org/ --dataset errors -F 'min(title)'
Error: Sentry rejected --field "min(title)" for dataset "errors".

min(title): column argument invalid: title is not a numeric column

Try:
  min() does not accept title on dataset "errors": it may not exist there, or may not have the type min() needs. Check the column name and its type.
# exit 21

I captured these outputs against a local stub that returns each 400 body.

via @BYK.

--

View Session [Sentry]

When Sentry rejects the function and argument of a user --field or --sort
(a string attribute in p75, transaction.duration on the transactions
dataset, a non-numeric errors column, an invalid count_if argument),
raise a ValidationError with the server detail and a recovery hint
instead of a raw 400 ApiError. Rejections of CLI-generated expressions
stay ApiErrors.

Refs CLI-10

Co-Authored-By: Miguel Betegon <miguel.betegon@sentry.io>
@vercel

vercel Bot commented Nov 4, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
sentry-local Ready Ready Preview Nov 4, 2026 9:58pm UTC

Request Review

@github-actions github-actions Bot added the risk: medium PR risk score: medium label Nov 4, 2026
The Events endpoint returns event_search.py parser failures unwrapped
("Invalid quote at ...", "Parse error at ...", '"is:" queries are not
supported ...', "timestamp: Invalid date: ..."). Only the issue-search
wrapper "Error parsing search query" was recognized, so a typo in a
user's explore/span/log/trace --query surfaced as a raw 400 and was
reported to Sentry as a CLI bug (CLI-10).

Recognize the grammar-level parser messages too, so toSearchQueryError
turns them into a query ValidationError when the user supplied --query.

Co-Authored-By: Miguel Betegon <miguel.betegon@sentry.io>
Comment thread packages/cli/src/lib/explore-errors.ts Resolved
Replace the list of parser message patterns with the
"invalid_search_query" code the Events endpoint now returns next to
"detail" (getsentry/sentry#129957). ApiError keeps the error body's code;
its optional enriched403 flag moves into an options object to make room.

Refs CLI-10
…-parse-errors

# Conflicts:
#	packages/cli/src/lib/api/infrastructure.ts
BYK pushed a commit to getsentry/sentry that referenced this pull request Nov 4, 2026
When a query function rejects one of its arguments, the 400 now also
carries `code`, `function`, and `argument` alongside `detail`. Clients
can then tell which `field`/`orderby` expression to fix without
regex-matching the message. `detail` stays the same, so existing
consumers aren't affected.

This covers both resolvers:
- EAP (`search/eap/resolver.py`): argument has the wrong type, isn't in
the allowlist, or fails a validator.
- Discover (`search/events/fields.py`): `<fn>: <arg> argument invalid:
…`.

Context: the Sentry CLI currently regex-matches these messages to tell a
user's typo apart from a CLI bug
([getsentry/toolkit#1927](getsentry/toolkit#1927 (comment)),
CLI-10). Once this lands, the CLI can key off
`code`/`function`/`argument` instead.

### Before

```
GET /api/0/organizations/org-slug/events/?dataset=spans&field=p75(span.description)
400 {"detail": "span.description is invalid for parameter 1 in p75. Its a string type field, but it must be one of these types: {...}"}

GET /api/0/organizations/org-slug/events/?dataset=errors&field=min(title)
400 {"detail": "min: column argument invalid: title is not a numeric column"}
```

### After

```
GET /api/0/organizations/org-slug/events/?dataset=spans&field=p75(span.description)
400 {"detail": "span.description is invalid for parameter 1 in p75. Its a string type field, but it must be one of these types: {...}",
     "code": "invalid_function_argument", "function": "p75", "argument": "span.description"}

GET /api/0/organizations/org-slug/events/?dataset=errors&field=min(title)
400 {"detail": "min: column argument invalid: title is not a numeric column",
     "code": "invalid_function_argument", "function": "min", "argument": "title"}
```

`argument` is the argument exactly as the client sent it (e.g.
`tags[foo,number]`), not the resolved alias.

<!-- junior-request-attribution:start -->
via @BYK.
<!-- junior-request-attribution:end -->

<!-- junior-session-footer:start -->
<!-- junior-conversation-id:agent%3A61f1063c1e971e700aaa2c3a3c12100a -->

--

[View
Session](https://junior-prod.sentry.dev/conversations/agent%3A61f1063c1e971e700aaa2c3a3c12100a)
[[Sentry]](https://sentry.sentry.io/explore/conversations/agent%3A61f1063c1e971e700aaa2c3a3c12100a/?project=4510944073809921)

<!-- junior-session-footer:end -->

Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com>
…rs' into fix/cli-explore-attribute-type-errors
Replace the detail-message patterns with the structured fields that
getsentry/sentry#129954 adds to function-argument 400s: code
invalid_function_argument plus function and argument. A 400 without the
code stays an ApiError.
@sentry-junior

sentry-junior Bot commented Nov 4, 2026

Copy link
Copy Markdown
Contributor Author

Done. getsentry/sentry#129954 (merged) now returns "code": "invalid_function_argument" with function and argument for these 400s. This PR reads those fields and drops REJECTED_ARGUMENT_PATTERNS. A 400 without the code stays an ApiError, and there's a test for that. This builds on #1936 for ApiError.code, so merge that first.

…bute-type-errors

# Conflicts:
#	packages/cli/src/lib/api/infrastructure.ts
#	packages/cli/src/lib/errors.ts
#	packages/cli/test/lib/api-client.coverage.test.ts
#	packages/cli/test/lib/api/infrastructure.test.ts
Comment thread packages/cli/src/lib/explore-errors.ts Resolved
@BYK
BYK enabled auto-merge (squash) November 4, 2026 21:56
@BYK
BYK merged commit 68a5856 into main Nov 4, 2026
27 checks passed
@BYK
BYK deleted the fix/cli-explore-attribute-type-errors branch November 4, 2026 22:11
BYK added a commit to getsentry/sentry that referenced this pull request Nov 4, 2026
#129981)

The `invalid_function_argument` 400 (#129954) can now include a `hint`
that suggests a fix. `InvalidFunctionArgumentQuery` takes an optional
`hint`, and `handle_query_errors` adds it to the body only when it's
set. All other bodies stay the same.

The first hint is for `transaction.duration` passed to a numeric
function on the spans dataset. Transactions are stored as spans, which
keep their duration in `span.duration`. This moves the hint out of the
Sentry CLI, as decided on
[getsentry/toolkit#1927](getsentry/toolkit#1927 (comment))
(CLI-10).

### Before

```
GET /api/0/organizations/org-slug/events/?dataset=spans&field=sum(transaction.duration)
400 {"detail": "transaction.duration is invalid for parameter 1 in sum. Its a string type field, but it must be one of these types: {...}",
     "code": "invalid_function_argument", "function": "sum", "argument": "transaction.duration"}
```

### After

```
GET /api/0/organizations/org-slug/events/?dataset=spans&field=sum(transaction.duration)
400 {"detail": "transaction.duration is invalid for parameter 1 in sum. Its a string type field, but it must be one of these types: {...}",
     "code": "invalid_function_argument", "function": "sum", "argument": "transaction.duration",
     "hint": "Use span.duration instead of transaction.duration on the spans dataset."}
```

<!-- junior-request-attribution:start -->
via @BYK.
<!-- junior-request-attribution:end -->

<!-- junior-session-footer:start -->
<!-- junior-conversation-id:agent%3A61f1063c1e971e700aaa2c3a3c12100a -->

--

[View
Session](https://junior-prod.sentry.dev/conversations/agent%3A61f1063c1e971e700aaa2c3a3c12100a)
[[Sentry]](https://sentry.sentry.io/explore/conversations/agent%3A61f1063c1e971e700aaa2c3a3c12100a/?project=4510944073809921)

<!-- junior-session-footer:end -->

Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com>
Co-authored-by: BYK <126780+BYK@users.noreply.github.com>
BYK added a commit that referenced this pull request Nov 4, 2026
…nts (#1966)

Follow-up to #1927 (CLI-10), as decided in [this review
thread](#1927 (comment)).

The `invalid_function_argument` 400 can now carry a `hint`
([getsentry/sentry#129981](getsentry/sentry#129981)).
`readErrorFields` reads it next to `function` and `argument`, and
`sentry explore` shows it as the suggestion. The CLI's own
`transaction.duration` → `span.duration` mapping is gone, since the
server owns that text now. The `tags[X,number]` / `sentry attribute
list` hint names CLI commands and flags, so it stays in the CLI as the
fallback when there's no `hint`.

Until sentry#129981 deploys, `transaction.duration` gets the generic
fallback hint instead of the `span.duration` one.

### Before

```
$ sentry explore my-org/ --dataset transactions -F 'sum(transaction.duration)'
Error: Sentry rejected --field "sum(transaction.duration)" for dataset "spans".

transaction.duration is invalid for parameter 1 in sum. Its a string type field, but it must be one of these types: {'number', 'duration'}

Try:
  Use span.duration instead of transaction.duration. Transactions are queried as spans, which store their duration in span.duration.
```

### After

With the server's `hint`:

```
$ sentry explore my-org/ --dataset transactions -F 'sum(transaction.duration)'
Error: Sentry rejected --field "sum(transaction.duration)" for dataset "spans".

transaction.duration is invalid for parameter 1 in sum. Its a string type field, but it must be one of these types: {'number', 'duration'}

Try:
  Use span.duration instead of transaction.duration on the spans dataset.
```

Without it (before sentry#129981 deploys):

```
Try:
  sum() does not accept transaction.duration on dataset "spans": it may not exist there, or may not have the type sum() needs. Reference a numeric custom attribute with its type, e.g. tags[transaction.duration,number]; to check the key and its type, run: sentry attribute list <org>/ transaction.duration --dataset spans
```

<!-- junior-request-attribution:start -->
via @BYK.
<!-- junior-request-attribution:end -->

<!-- junior-session-footer:start -->
<!-- junior-conversation-id:agent%3A61f1063c1e971e700aaa2c3a3c12100a -->

--

[View
Session](https://junior-prod.sentry.dev/conversations/agent%3A61f1063c1e971e700aaa2c3a3c12100a)
[[Sentry]](https://sentry.sentry.io/explore/conversations/agent%3A61f1063c1e971e700aaa2c3a3c12100a/?project=4510944073809921)

<!-- junior-session-footer:end -->

Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com>
Co-authored-by: BYK <126780+BYK@users.noreply.github.com>

This branch was successfully deployed

1 active deployment
Preview — a93b26a8 Deployed Nov 4, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

risk: medium PR risk score: medium

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant