Skip to content

fix(cli): Silence user-supplied search query syntax errors and validate failure_rate - #1666

Closed
sentry[bot] wants to merge 2 commits into
mainfrom
seer/fix/cli-1cz-silence-query-syntax-and-allow-failure-rate
Closed

sentry[bot] wants to merge 2 commits into
mainfrom
seer/fix/cli-1cz-silence-query-syntax-and-allow-failure-rate

Conversation

@sentry

@sentry sentry Bot commented Oct 25, 2026

Copy link
Copy Markdown
Contributor

Root Cause

When the Sentry server rejects a user-supplied search query (e.g. is:403, category:csp, status:401), toSearchQueryError wraps the 400 ApiError into a ValidationError with field "query". Because classifySilenced did not handle this field, every rejected user query was forwarded to Sentry as an issue. deriveErrorKind keyed on error.field ?? ..., so all ValidationErrors with field "query" received cli_error.kind: "query" and were grouped into a single catch-all issue (CLI-1CZ). This bucketed together user query typos (~83% of volume) and genuine CLI validation bugs, such as sentry dashboard widget add rejecting failure_rate because it was missing from SPAN_AGGREGATE_FUNCTIONS.

Changes

  1. Silence user query syntax errors (src/lib/errors.ts, src/lib/error-reporting.ts):

    • In toSearchQueryError, construct the ValidationError with USER_QUERY_SYNTAX_FIELD = "query.user_syntax".
    • In classifySilenced, return "user_query_syntax" for errors with this field. The CLI already displays the server's detail and search syntax suggestions; queries the CLI builds itself still fail as 400 ApiErrors and remain captured.
    • In recordSilencedError, emit Sentry.logger.info("cli.query_syntax_silenced") with the redacted parse detail and count the event under the existing cli.error.silenced metric.
  2. Disambiguate remaining query ValidationErrors (src/lib/error-reporting.ts):

    • In deriveErrorKind, when error.field === "query", suffix the kind with the message prefix (query: <first words>) so genuine CLI defects (e.g. unknown aggregates, empty alert query) do not group into one catch-all.
  3. Add failure_rate to span aggregates (src/types/dashboard.ts):

    • Include failure_rate in SPAN_AGGREGATE_FUNCTIONS so dashboard span widget creation accepts it.
  4. Tests:

    • Updated test/lib/errors.test.ts and test/commands/issue/list.test.ts to expect query.user_syntax.
    • Added silencing, metric emission, and kind derivation tests in test/lib/error-reporting.test.ts.
    • Added aggregate validation tests for failure_rate in test/types/dashboard.test.ts.

Fixes CLI-1CZ

@sentry <feedback>: Autofix iterates on these changes
@sentry stop iterating: Autofix stops iterating on this run

This PR was automatically generated by Sentry. You can adjust this setting at any time.

@vercel

vercel Bot commented Oct 25, 2026 •

Copy link
Copy Markdown

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

1 Skipped Deployment
Project Deployment Actions Updated
sentry-local Skipped Skipped Oct 26, 2026 4:14pm UTC

Request Review

@github-actions github-actions Bot added the risk: medium PR risk score: medium label Oct 25, 2026
Replace the `void logError` catch, which sidesteps the no-silent-catch
rule, with a log.debug call as required by packages/cli/AGENTS.md.

@sentry-junior sentry-junior Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Junior review: I checked this against the issue data and the producer and consumer contracts. It looks justified. I pushed one small fix in 4e342f9.

What I verified

  • CLI-1CZ breakdown (from the issue tags, 1837 events, all cli_error.kind=query): issue.list 1524, issues 11, feedback.list 1. That is about 83.6% coming through toSearchQueryError with a user-supplied --query. The rest are CLI-side validators: dashboard.widget.add 187 and .edit 32 (unknown aggregate, invalid tracemetrics aggregate), and alert.metrics.create 82 (query cannot be empty.). So the description's claim holds up.
  • failure_rate is a real defect. It is the most common unknown aggregate in a sample of 100 widget.add events (about 40 hits), including on 0.47.0. Sentry supports it for spans: it is defined in src/sentry/search/eap/spans/formulas.py and appears in ALLOWED_EXPLORE_VISUALIZE_AGGREGATES in static/app/utils/fields/index.ts. #1295 dropped it from the span list only because it was already in the discover list, and #1515 later removed that discover entry, so spans stopped accepting it.
  • The deriveErrorKind change splits the issue as intended. The cli project's fingerprint rules group ValidationErrors by {{ tags.cli_error.kind }}, so query: <prefix> creates separate groups. extractMessagePrefix strips quoted values, which keeps the number of groups small (Unknown aggregate function, Invalid tracemetrics aggregate, query cannot be).

Correction pushed

  • The new catch in recordSilencedError used void logError. That gets past the no-silent-catch lint rule without doing what packages/cli/AGENTS.md asks for, which is to log or re-throw. It now calls log.debug. These tests pass locally (397 tests): test/lib/error-reporting.test.ts, errors.test.ts, types/dashboard.test.ts, commands/issue/list.test.ts. oxlint and oxfmt are clean on the file.

For the merging maintainer (not blockers)

  • This reverses the default chosen in getsentry/cli#1154, which kept user-query ValidationErrors captured on purpose as a query-linting UX signal (and offered to silence them separately). The cli.error.silenced metric and the cli.query_syntax_silenced log keep the volume visible, which follows the pattern used for CLI-1JY, CLI-1FN and CLI-RP. Please confirm that's the policy you want.
  • flags.query has already been through sanitizeQuery, which applies bracket and comma fixes, numeric project: rewriting and OR rewriting. A 400 caused by one of those rewrites would also be silenced. The structured log keeps the server detail, so this is still traceable.
  • Since ERROR_AGGREGATE_FUNCTIONS spreads the span list, error-events now accepts failure_rate too. That list already accepts other span-only functions (p50 etc.), so this doesn't make it newly loose.

@BYK

BYK commented Oct 26, 2026

Copy link
Copy Markdown
Member

We don't want this to keep an eye how users are failing, how frequently, and to see if we can actually improve the situation.

@BYK BYK closed this Oct 26, 2026

This branch was previously deployed

1 inactive deployment
Preview — 4e342f98 Deployed Oct 26, 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