Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
208 changes: 140 additions & 68 deletions .lore.md

Large diffs are not rendered by default.

5 changes: 4 additions & 1 deletion src/commands/event/list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
hasPreviousPage,
resolveCursor,
} from "../../lib/db/pagination.js";
import { ContextError } from "../../lib/errors.js";
import { ContextError, toSearchQueryError } from "../../lib/errors.js";
import { CommandOutput } from "../../lib/formatters/output.js";
import { buildListCommand, paginationHint } from "../../lib/list-command.js";
import { withProgress } from "../../lib/polling.js";
Expand Down Expand Up @@ -155,6 +155,9 @@ export const listCommand = buildListCommand("event", {
full: flags.full,
cursor,
...timeRangeToApiParams(timeRange),
}).catch((error: unknown): never => {
// An unparseable user --query is a user input mistake, not a CLI bug.
throw toSearchQueryError(error, flags.query);
})
);

Expand Down
10 changes: 8 additions & 2 deletions src/commands/explore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import {
hasPreviousPage,
resolveCursor,
} from "../lib/db/pagination.js";
import { ValidationError } from "../lib/errors.js";
import { toSearchQueryError, ValidationError } from "../lib/errors.js";
import { filterFields } from "../lib/formatters/json.js";
import { buildMetaColumns } from "../lib/formatters/meta-table.js";
import { CommandOutput } from "../lib/formatters/output.js";
Expand Down Expand Up @@ -792,7 +792,13 @@ export const exploreCommand = buildListCommand("explore", {
message: `Querying ${dataset} in ${project ? `${org}/${project}` : org}...`,
json: flags.json,
},
() => config.fetch({ cursor, limit: flags.limit, timeRange })
() =>
config
.fetch({ cursor, limit: flags.limit, timeRange })
.catch((error: unknown): never => {
// An unparseable user --query is a user input mistake, not a CLI bug.
throw toSearchQueryError(error, flags.query);
})
);

advancePaginationState(PAGINATION_KEY, contextKey, direction, nextCursor);
Expand Down
5 changes: 4 additions & 1 deletion src/commands/issue/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
hasPreviousPage,
resolveCursor,
} from "../../lib/db/pagination.js";
import { ContextError } from "../../lib/errors.js";
import { ContextError, toSearchQueryError } from "../../lib/errors.js";
import { CommandOutput } from "../../lib/formatters/output.js";
import { buildListCommand, paginationHint } from "../../lib/list-command.js";
import { withProgress } from "../../lib/polling.js";
Expand Down Expand Up @@ -137,6 +137,9 @@ export const eventsCommand = buildListCommand("issue", {
full: flags.full,
cursor,
...timeRangeToApiParams(timeRange),
}).catch((error: unknown): never => {
// An unparseable user --query is a user input mistake, not a CLI bug.
throw toSearchQueryError(error, flags.query);
})
);

Expand Down
16 changes: 16 additions & 0 deletions src/commands/issue/list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import {
ApiError,
ContextError,
toSearchQueryError,

Check warning on line 43 in src/commands/issue/list.ts

View check run for this annotation

@sentry/warden / warden: find-bugs

`handleProjectMode` in span/list.ts missing `toSearchQueryError` wrapper, causing user query 400s to be reported as CLI bugs

In `src/commands/span/list.ts`, `handleProjectMode` calls `listSpans` without wrapping the error in `toSearchQueryError`, unlike `handleTraceMode`. After this PR's change, `isUserError` returns `false` for HTTP 400, so a user's invalid `--query` in project mode bypasses ValidationError conversion and gets reported to Sentry as a CLI defect.
ValidationError,
withAuthGuard,
} from "../../lib/errors.js";
Expand Down Expand Up @@ -901,6 +902,13 @@
error: unknown,
flags: Pick<ListFlags, "query" | "period" | "sort">
): never {
// A user-supplied --query the server cannot parse is a user input mistake,
// not a CLI bug: surface it as a ValidationError. A 400 with no user --query
// (the CLI built a bad query) falls through and stays a reported ApiError.
const queryError = toSearchQueryError(error, flags.query);
if (queryError !== error) {
throw queryError;
}
if (error instanceof ApiError) {
if (error.status === 400) {
throw new ApiError(
Expand Down Expand Up @@ -1119,6 +1127,14 @@
const { error: first } = failures[0]!;
const prefix = `Failed to fetch issues from ${targets.length} project(s)`;

// A user-supplied --query the server cannot parse is a user input mistake,
// not a CLI bug — surface it as a ValidationError before wrapping as a
// multi-project ApiError. CLI-authored 400s fall through and stay reported.
const queryError = toSearchQueryError(first, flags.query);
if (queryError !== first) {
throw queryError;
}

// Propagate ApiError so telemetry sees the original status code.
// For 400 errors, append actionable suggestions since the user's query
// or parameters are likely malformed. Common causes: invalid Sentry
Expand Down
4 changes: 4 additions & 0 deletions src/commands/log/list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import {
AuthError,
stringifyUnknown,
toSearchQueryError,

Check warning on line 25 in src/commands/log/list.ts

View check run for this annotation

@sentry/warden / warden: find-bugs

`executeTraceSingleFetch` skips `toSearchQueryError`, leaving user-query 400s as raw `ApiError` in trace mode

In `log/list.ts`, `executeSingleFetch` wraps its `listLogs` call with `.catch((error) => { throw toSearchQueryError(error, flags.query); })` so an invalid user `--query` surfaces as an actionable `ValidationError` instead of being reported to Sentry as a CLI bug. The parallel `executeTraceSingleFetch` (used by `sentry log list <trace>`) calls `listTraceLogs` at line 487 without this guard. A user-supplied `--query` rejected by the server with a 400 parse error therefore surfaces as a raw `ApiError(400)` and is misclassified as a CLI defect, getting reported to Sentry. The sibling command `trace/logs.ts` (line 213) wraps the identical `listTraceLogs` call with `toSearchQueryError(error, flags.query)`, confirming the intended pattern. Fix: wrap the `listTraceLogs` call in `executeTraceSingleFetch` with `.catch((error) => { throw toSearchQueryError(error, flags.query); })`.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

executeTraceSingleFetch skips toSearchQueryError, leaving user-query 400s as raw ApiError in trace mode

In log/list.ts, executeSingleFetch wraps its listLogs call with .catch((error) => { throw toSearchQueryError(error, flags.query); }) so an invalid user --query surfaces as an actionable ValidationError instead of being reported to Sentry as a CLI bug. The parallel executeTraceSingleFetch (used by sentry log list <trace>) calls listTraceLogs at line 487 without this guard. A user-supplied --query rejected by the server with a 400 parse error therefore surfaces as a raw ApiError(400) and is misclassified as a CLI defect, getting reported to Sentry. The sibling command trace/logs.ts (line 213) wraps the identical listTraceLogs call with toSearchQueryError(error, flags.query), confirming the intended pattern. Fix: wrap the listTraceLogs call in executeTraceSingleFetch with .catch((error) => { throw toSearchQueryError(error, flags.query); }).

Evidence
  • executeSingleFetch (log/list.ts:183-188) catches listLogs failures and rethrows via toSearchQueryError(error, flags.query).
  • executeTraceSingleFetch (log/list.ts:487) awaits listTraceLogs(org, traceId, { query, ... }) with no .catch, so a parse-400 propagates unchanged.
  • query is built from the user value: buildProjectQuery(flags.query, projectFilter) (line 486), so an invalid user --query reaches the endpoint.
  • trace/logs.ts:206-214 wraps the same listTraceLogs call with toSearchQueryError(error, flags.query), showing the guard was intended for trace-logs queries.
Also found at 2 additional locations
  • src/commands/log/list.ts:184
  • src/lib/telemetry.ts:364-366

Identified by Warden find-bugs · SWM-6YD

ValidationError,
} from "../../lib/errors.js";
import {
Expand Down Expand Up @@ -180,6 +181,9 @@
...timeRangeToApiParams(timeRange),
sort: flags.sort,
extraFields: flags.fields,
}).catch((error: unknown): never => {

Check warning on line 184 in src/commands/log/list.ts

View check run for this annotation

@sentry/warden / warden: find-bugs

[SWM-6YD] `executeTraceSingleFetch` skips `toSearchQueryError`, leaving user-query 400s as raw `ApiError` in trace mode (additional location)

In `log/list.ts`, `executeSingleFetch` wraps its `listLogs` call with `.catch((error) => { throw toSearchQueryError(error, flags.query); })` so an invalid user `--query` surfaces as an actionable `ValidationError` instead of being reported to Sentry as a CLI bug. The parallel `executeTraceSingleFetch` (used by `sentry log list <trace>`) calls `listTraceLogs` at line 487 without this guard. A user-supplied `--query` rejected by the server with a 400 parse error therefore surfaces as a raw `ApiError(400)` and is misclassified as a CLI defect, getting reported to Sentry. The sibling command `trace/logs.ts` (line 213) wraps the identical `listTraceLogs` call with `toSearchQueryError(error, flags.query)`, confirming the intended pattern. Fix: wrap the `listTraceLogs` call in `executeTraceSingleFetch` with `.catch((error) => { throw toSearchQueryError(error, flags.query); })`.
// An unparseable user --query is a user input mistake, not a CLI bug.
throw toSearchQueryError(error, flags.query);

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.

Log follow mode missing handler

Medium Severity

Non-follow fetches use toSearchQueryError on listLogs failures, but the --follow path calls listLogs with no conversion. A bad --query while streaming logs still surfaces as a reported ApiError(400) instead of a fielded ValidationError.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit a44094d. Configure here.

});

const periodLabel =
Expand Down
4 changes: 4 additions & 0 deletions src/commands/span/list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
hasPreviousPage,
resolveCursor,
} from "../../lib/db/pagination.js";
import { toSearchQueryError } from "../../lib/errors.js";

Check warning on line 23 in src/commands/span/list.ts

View check run for this annotation

@sentry/warden / warden: find-bugs

[XQG-G6W] `handleProjectMode` in span/list.ts missing `toSearchQueryError` wrapper, causing user query 400s to be reported as CLI bugs (additional location)

In `src/commands/span/list.ts`, `handleProjectMode` calls `listSpans` without wrapping the error in `toSearchQueryError`, unlike `handleTraceMode`. After this PR's change, `isUserError` returns `false` for HTTP 400, so a user's invalid `--query` in project mode bypasses ValidationError conversion and gets reported to Sentry as a CLI defect.
import {
type FlatSpan,
formatSpanTable,
Expand Down Expand Up @@ -390,6 +391,9 @@
...timeRangeToApiParams(timeRange),
extraFields: extraApiFields,
allProjects: true,
}).catch((error: unknown): never => {
// An unparseable user --query is a user input mistake, not a CLI bug.
throw toSearchQueryError(error, flags.query);

Check warning on line 396 in src/commands/span/list.ts

View check run for this annotation

@sentry/warden / warden: find-bugs

[XQG-G6W] `handleProjectMode` in span/list.ts missing `toSearchQueryError` wrapper, causing user query 400s to be reported as CLI bugs (additional location)

In `src/commands/span/list.ts`, `handleProjectMode` calls `listSpans` without wrapping the error in `toSearchQueryError`, unlike `handleTraceMode`. After this PR's change, `isUserError` returns `false` for HTTP 400, so a user's invalid `--query` in project mode bypasses ValidationError conversion and gets reported to Sentry as a CLI defect.

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.

Span project mode missing handler

Medium Severity

handleTraceMode wraps listSpans with toSearchQueryError, but handleProjectMode does not. With --query in project mode, a server search-parse 400 stays a raw ApiError(400), so it is reported as a CLI defect and skips the ValidationError UX the PR adds elsewhere.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit a44094d. Configure here.

})
);

Comment on lines 391 to 399

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.

Bug: The span list command in project mode is missing the toSearchQueryError() error handler, causing invalid user queries to be reported as CLI bugs instead of user errors.
Severity: MEDIUM

Suggested Fix

Wrap the listSpans() API call within handleProjectMode in src/commands/span/list.ts with a .catch() block that uses toSearchQueryError, similar to the implementation in handleTraceMode. This will ensure invalid search queries from users are correctly handled as input errors.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.

Location: src/commands/span/list.ts#L391-L399

Potential issue: The `span list` command has two modes: trace mode and project mode.
While an error handler using `toSearchQueryError` was added to `handleTraceMode` to
correctly handle invalid user search queries, the same handler is missing for the
`listSpans()` call in `handleProjectMode`. As a result, if a user runs `sentry span list
-q "invalid_query"`, the API's 400 error is not converted into a user-friendly
`ValidationError`. Instead, it's treated as an unhandled CLI bug and reported to Sentry,
contradicting the PR's goal of gracefully handling user query typos.

Did we get this right? 👍 / 👎 to inform future reviews.

Expand Down
4 changes: 4 additions & 0 deletions src/commands/trace/list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
hasPreviousPage,
resolveCursor,
} from "../../lib/db/pagination.js";
import { toSearchQueryError } from "../../lib/errors.js";

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

User-query 400s in log/list, span/list, event/list, and issue/events now falsely reported to Sentry as CLI bugs

Removing isSearchQueryParseError from classifySilenced, isUserError, and isUserApiError requires every command with a user --query flag to call toSearchQueryError before re-throwing API errors. log/list.ts (passes flags.query to listLogs), span/list.ts (passes translateSpanQuery(flags.query) to listSpans), event/list.ts, and issue/events.ts were not updated, so a user typing an invalid --query in those commands will produce an ApiError(400) that is now captured by Sentry as a CLI defect instead of being surfaced as a ValidationError.

Evidence
  • classifySilenced in error-reporting.ts now has no special case for 400: only error.status > 400 && error.status < 500 is silenced, so ApiError(400) with detail 'Error parsing search query' reaches captureException.
  • isUserError in errors.ts similarly excludes 400 (error.status > 400 && error.status < 500), so the upgrade nudge fires.
  • log/list.ts:178 passes query: flags.query to listLogs with no surrounding toSearchQueryError call confirmed by grep returning no matches.
  • span/list.ts:451 passes the translated user query to listSpans with no toSearchQueryError call.
  • The three commands that were updated (issue/list.ts, explore.ts, trace/list.ts) all wrap their API .catch with throw toSearchQueryError(error, flags.query), confirming the required pattern.
Also found at 2 additional locations
  • src/lib/errors.ts:882-887
  • src/lib/telemetry.ts:363-365

Identified by Warden find-bugs · DS5-SH4

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Good catch — fixed in a44094d. Wired toSearchQueryError(error, flags.query) into the remaining commands that hit the events/logs/spans search backend (which emits the Error parsing search query marker): log list, span list, event list, issue events, and trace logs. Other query-forwarding commands (replay/alert/dashboard) use different backends that don't emit that marker, so they were never silenced and toSearchQueryError is a no-op there.

import { formatTraceTable } from "../../lib/formatters/index.js";
import { filterFields } from "../../lib/formatters/json.js";
import { CommandOutput } from "../../lib/formatters/output.js";
Expand Down Expand Up @@ -289,6 +290,9 @@ export const listCommand = buildListCommand("trace", {
sort: flags.sort,
cursor,
...timeRangeToApiParams(timeRange),
}).catch((error: unknown): never => {
// An unparseable user --query is a user input mistake, not a CLI bug.
throw toSearchQueryError(error, flags.query);
})
);

Expand Down
4 changes: 4 additions & 0 deletions src/commands/trace/logs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
} from "../../lib/arg-parsing.js";
import { openInBrowser } from "../../lib/browser.js";
import { buildCommand } from "../../lib/command.js";
import { toSearchQueryError } from "../../lib/errors.js";
import { filterFields } from "../../lib/formatters/json.js";
import { formatLogTable } from "../../lib/formatters/log.js";
import { CommandOutput, formatFooter } from "../../lib/formatters/output.js";
Expand Down Expand Up @@ -207,6 +208,9 @@ export const logsCommand = buildCommand({
limit: flags.limit,
query,
sort: flags.sort,
}).catch((error: unknown): never => {
// An unparseable user --query is a user input mistake, not a CLI bug.
throw toSearchQueryError(error, flags.query);
})
);

Expand Down
15 changes: 5 additions & 10 deletions src/lib/error-reporting.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
DeviceFlowError,
HostScopeError,
isNetworkError,
isSearchQueryParseError,
OutputError,
ResolutionError,
SeerError,
Expand All @@ -51,7 +50,6 @@
| "context_missing"
| "auth_expected"
| "api_user_error"
| "api_query_error"
| "network_error";

/**
Expand Down Expand Up @@ -93,16 +91,13 @@
}
if (error instanceof ApiError && error.status > 400 && error.status < 500) {
return "api_user_error";
}
// A 400 normally signals a malformed request the CLI built (a code defect),
// so it is captured by default. The exception: when the server reports it
// could not parse the user's search query, the `--query` syntax is wrong
// (CLI-FA: ~450 users across issue/explore/trace list). That is a user input
// error, and the API already returns an actionable message, so silence it.
if (error instanceof ApiError && isSearchQueryParseError(error)) {
return "api_query_error";
}
// A 400 (Bad Request) signals a malformed request the CLI built — a code
// defect — so it is always captured. A user's unparseable `--query` is NOT a
// 400 here: it is converted to a ValidationError at the command boundary
// (toSearchQueryError) so the user gets an actionable message and the bug
// signal for CLI-authored bad queries is preserved (CLI-FA).
return null;

Check warning on line 100 in src/lib/error-reporting.ts

View check run for this annotation

@sentry/warden / warden: find-bugs

[5YV-26Z] replay/list.ts never calls toSearchQueryError, so user --query 400s are now misclassified as CLI bugs (additional location)

The safety net (`isSearchQueryParseError` in `isUserError`) was removed under the assumption that every command boundary converts search-query 400s via `toSearchQueryError`, but `replay/list.ts` supports `--query` and calls `listReplays` without any `toSearchQueryError` wrapping — so invalid user queries in that command will now be reported to Sentry as CLI defects instead of surfaced as validation errors.
}

/** Emit a metric for silenced errors so volume remains visible. */
Expand Down
67 changes: 61 additions & 6 deletions src/lib/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -780,6 +780,63 @@
);
}

/**
* Standard actionable hint shown when a user-supplied search query is rejected.
* Exported so command boundaries can reuse it alongside command-specific hints.
*/
export const SEARCH_QUERY_HELP =
"Check your --query syntax (Sentry search reference: https://docs.sentry.io/concepts/search/)";

/**
* Convert a Sentry search-query parse failure into a {@link ValidationError}
* when the user actually supplied the query; otherwise return the error
* unchanged.
*
* HTTP 400 means the server rejected our request as malformed, which has two
* very different causes that are only distinguishable at the command boundary
* (where `flags.query` is in scope):
*
* - **The user typed an invalid `--query`** — a user input mistake. We surface
* a clean, actionable {@link ValidationError} (correct exit code, no "CLI
* bug" upgrade nudge) instead of a raw "400 Bad Request".
* - **The CLI built the query itself** (no user `--query`) — a genuine CLI
* defect. The original {@link ApiError} (status 400) is returned untouched so
* it stays reported to Sentry as the bug it is.
*
* This is why search-query 400s are no longer special-cased in the error
* classifiers (`classifySilenced`, {@link isUserError}, `isUserApiError`):
* silencing every "Error parsing search query" 400 also hid the CLI-authored
* ones, papering over real bugs.
*
* @param error - The error thrown by a list/search API call
* @param userQuery - The user-supplied search query (`flags.query`), if any
* @param extraSuggestions - Optional command-specific hints appended after the
* standard search-syntax help
* @returns A {@link ValidationError} for user-query mistakes, else `error`
*/
export function toSearchQueryError(
error: unknown,
userQuery: string | undefined,
extraSuggestions: readonly string[] = []
): unknown {
if (
userQuery &&
error instanceof ApiError &&
isSearchQueryParseError(error)
) {
const lines: string[] = [];
if (error.detail) {
lines.push(error.detail, "");
}
lines.push("Suggestions:");
for (const suggestion of [SEARCH_QUERY_HELP, ...extraSuggestions]) {
lines.push(` • ${suggestion}`);
}
return new ValidationError(lines.join("\n"), "query");
}
return error;
}

/**
* Whether an error is a raw network-level fetch failure — the CLI could not
* reach the Sentry API at all (offline, DNS failure, connection
Expand Down Expand Up @@ -822,14 +879,12 @@
if (error.status === 0) {
return true;
}
// 400 usually means the CLI constructed a bad request, so it is treated as
// a CLI bug — except when the server reports the user's search query was
// unparseable, which is a user input mistake.
if (isSearchQueryParseError(error)) {
return true;
}
// 400 means the CLI constructed a bad request, which is a CLI bug — not a
// user error. (A user's unparseable `--query` is converted to a
// ValidationError at the command boundary via toSearchQueryError, so it
// never reaches here as an ApiError.)
return error.status > 400 && error.status < 500;
}

Check warning on line 887 in src/lib/errors.ts

View check run for this annotation

@sentry/warden / warden: find-bugs

[XQG-G6W] `handleProjectMode` in span/list.ts missing `toSearchQueryError` wrapper, causing user query 400s to be reported as CLI bugs (additional location)

In `src/commands/span/list.ts`, `handleProjectMode` calls `listSpans` without wrapping the error in `toSearchQueryError`, unlike `handleTraceMode`. After this PR's change, `isUserError` returns `false` for HTTP 400, so a user's invalid `--query` in project mode bypasses ValidationError conversion and gets reported to Sentry as a CLI defect.

Check warning on line 887 in src/lib/errors.ts

View check run for this annotation

@sentry/warden / warden: find-bugs

replay/list.ts never calls toSearchQueryError, so user --query 400s are now misclassified as CLI bugs

The safety net (`isSearchQueryParseError` in `isUserError`) was removed under the assumption that every command boundary converts search-query 400s via `toSearchQueryError`, but `replay/list.ts` supports `--query` and calls `listReplays` without any `toSearchQueryError` wrapping — so invalid user queries in that command will now be reported to Sentry as CLI defects instead of surfaced as validation errors.

if (
error instanceof AbortError ||
Expand Down
14 changes: 6 additions & 8 deletions src/lib/telemetry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
enrichEventWithGroupingTags,
reportCliError,
} from "./error-reporting.js";
import { ApiError, isSearchQueryParseError } from "./errors.js";
import { ApiError } from "./errors.js";
import { attachSentryReporter, logger } from "./logger.js";
import { getSentryBaseUrl, isSentrySaasUrl } from "./sentry-urls.js";
import { makeCompressedTransport } from "./telemetry/zstd-transport.js";
Expand Down Expand Up @@ -347,10 +347,10 @@
* Check if an error is a user-caused (401–499) API error.
*
* 401–499 errors are user errors — wrong issue IDs, no access, rate limited —
* not CLI bugs. 400 Bad Request is **excluded** because it usually indicates
* the CLI constructed a malformed API request, which is a code defect — except
* when the server reports the user's search query was unparseable, which is a
* user input mistake (kept in sync with `classifySilenced` / `isUserError`).
* not CLI bugs. 400 Bad Request is **excluded** because it indicates the CLI
* constructed a malformed API request, which is a code defect. (A user's
* unparseable `--query` is converted to a ValidationError at the command
* boundary via toSearchQueryError, so it never reaches here as an ApiError.)
*
* These should be recorded as span attributes for volume-spike detection in
* Discover, but should NOT be captured as Sentry exceptions.
Expand All @@ -361,11 +361,9 @@
if (!(error instanceof ApiError)) {
return false;
}
return (
isSearchQueryParseError(error) || (error.status > 400 && error.status < 500)
);
return error.status > 400 && error.status < 500;
}

Check warning on line 366 in src/lib/telemetry.ts

View check run for this annotation

@sentry/warden / warden: find-bugs

[SWM-6YD] `executeTraceSingleFetch` skips `toSearchQueryError`, leaving user-query 400s as raw `ApiError` in trace mode (additional location)

In `log/list.ts`, `executeSingleFetch` wraps its `listLogs` call with `.catch((error) => { throw toSearchQueryError(error, flags.query); })` so an invalid user `--query` surfaces as an actionable `ValidationError` instead of being reported to Sentry as a CLI bug. The parallel `executeTraceSingleFetch` (used by `sentry log list <trace>`) calls `listTraceLogs` at line 487 without this guard. A user-supplied `--query` rejected by the server with a 400 parse error therefore surfaces as a raw `ApiError(400)` and is misclassified as a CLI defect, getting reported to Sentry. The sibling command `trace/logs.ts` (line 213) wraps the identical `listTraceLogs` call with `toSearchQueryError(error, flags.query)`, confirming the intended pattern. Fix: wrap the `listTraceLogs` call in `executeTraceSingleFetch` with `.catch((error) => { throw toSearchQueryError(error, flags.query); })`.
/**
* Record a client API error as span attributes for Discover queryability.
*
Expand Down
Loading
Loading