From 732096824a324ab919a75629bce9f9df5ac5695e Mon Sep 17 00:00:00 2001 From: "sentry[bot]" <39604003+sentry[bot]@users.noreply.github.com> Date: Fri, 25 Sep 2026 22:42:00 +0000 Subject: [PATCH] fix(span): use ResolutionError for missing spans --- packages/cli/src/commands/span/view.ts | 31 +++++++++++++------- packages/cli/test/commands/span/view.test.ts | 18 +++++++----- 2 files changed, 32 insertions(+), 17 deletions(-) diff --git a/packages/cli/src/commands/span/view.ts b/packages/cli/src/commands/span/view.ts index e997697f8..2bebe1dde 100644 --- a/packages/cli/src/commands/span/view.ts +++ b/packages/cli/src/commands/span/view.ts @@ -13,7 +13,11 @@ import { } from "../../lib/api-client.js"; import { spansFlag } from "../../lib/arg-parsing.js"; import { buildCommand } from "../../lib/command.js"; -import { ContextError, ValidationError } from "../../lib/errors.js"; +import { + ContextError, + ResolutionError, + ValidationError, +} from "../../lib/errors.js"; import { type FoundSpan, findSpanById, @@ -430,11 +434,10 @@ export const viewCommand = buildCommand({ const spans = await getDetailedTrace(org, traceId); if (spans.length === 0) { - throw new ValidationError( - `No trace found with ID "${traceId}".\n\n` + - "The ID format is valid but no matching trace exists in this project. " + - "Check that you are querying the right org/project, or the trace may be past your plan's retention window." - ); + throw new ResolutionError(`Trace "${traceId}"`, "not found", USAGE_HINT, [ + "The ID format is valid but no matching trace exists in this project", + "Check that you are querying the right org/project, or the trace may be past your plan's retention window", + ]); } // Find each requested span @@ -455,11 +458,19 @@ export const viewCommand = buildCommand({ } if (results.length === 0) { + if (spanIds.length === 1) { + throw new ResolutionError( + `Span "${spanIds[0]}" in trace ${traceId}`, + "not found", + USAGE_HINT + ); + } const idList = formatIdList(spanIds); - throw new ValidationError( - spanIds.length === 1 - ? `No span found with ID "${spanIds[0]}" in trace ${traceId}.` - : `No spans found with any of the following IDs in trace ${traceId}:\n${idList}` + throw new ResolutionError( + `Spans in trace ${traceId}`, + "none of the requested IDs were found", + USAGE_HINT, + [`Requested IDs:\n${idList}`] ); } diff --git a/packages/cli/test/commands/span/view.test.ts b/packages/cli/test/commands/span/view.test.ts index 7a3d8bd30..03c80e953 100644 --- a/packages/cli/test/commands/span/view.test.ts +++ b/packages/cli/test/commands/span/view.test.ts @@ -26,7 +26,11 @@ vi.mock("../../../src/lib/api-client.js", async (importOriginal) => { import * as apiClient from "../../../src/lib/api-client.js"; import { DEFAULT_SENTRY_URL } from "../../../src/lib/constants.js"; import { setOrgRegion } from "../../../src/lib/db/regions.js"; -import { ContextError, ValidationError } from "../../../src/lib/errors.js"; +import { + ContextError, + ResolutionError, + ValidationError, +} from "../../../src/lib/errors.js"; import { validateSpanId } from "../../../src/lib/hex-id.js"; vi.mock("../../../src/lib/resolve-target.js", async (importOriginal) => { @@ -414,7 +418,7 @@ describe("viewCommand.func", () => { expect(output).toContain("http.server"); }); - test("throws ValidationError when trace has no spans", async () => { + test("throws ResolutionError when trace has no spans", async () => { getDetailedTraceSpy.mockResolvedValue([]); const { context } = createContext(); @@ -429,10 +433,10 @@ describe("viewCommand.func", () => { VALID_TRACE_ID, VALID_SPAN_ID ) - ).rejects.toThrow(ValidationError); + ).rejects.toThrow(ResolutionError); }); - test("throws ValidationError when span ID not found in trace", async () => { + test("throws ResolutionError when span ID not found in trace", async () => { getDetailedTraceSpy.mockResolvedValue([makeTraceSpan("0000000000000000")]); const { context } = createContext(); @@ -447,7 +451,7 @@ describe("viewCommand.func", () => { VALID_TRACE_ID, VALID_SPAN_ID ) - ).rejects.toThrow(ValidationError); + ).rejects.toThrow(ResolutionError); }); test("uses explicit org/project from slash-separated arg", async () => { @@ -545,7 +549,7 @@ describe("viewCommand.func", () => { ).rejects.toThrow(ContextError); }); - test("throws ValidationError for multiple missing span IDs", async () => { + test("throws ResolutionError for multiple missing span IDs", async () => { getDetailedTraceSpy.mockResolvedValue([makeTraceSpan("0000000000000000")]); const { context } = createContext(); @@ -558,6 +562,6 @@ describe("viewCommand.func", () => { VALID_SPAN_ID, VALID_SPAN_ID_2 ) - ).rejects.toThrow(ValidationError); + ).rejects.toThrow(ResolutionError); }); });