Skip to content

Commit ab5913b

Browse files
committed
fix: enrich event 404 errors with retention and format suggestions (CLI-6F)
When `sentry event view` gets a 404 Not Found, the generic 'Failed to get event: 404 Not Found' doesn't help users diagnose the cause. This affects 54 users (CLI-6F). Extracted event fetching into a fetchEventWithContext() helper that catches 404 errors and throws a ResolutionError with suggestions: - Data retention policies may have deleted the event - Verify the event ID is a 32-char hex string - Check if the event belongs to a different project This also fixes the biome complexity lint by extracting the try-catch into a separate function instead of adding it inline to func().
1 parent b74d1d6 commit ab5913b

File tree

1 file changed

+48
-4
lines changed

1 file changed

+48
-4
lines changed

src/commands/event/view.ts

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import {
2222
} from "../../lib/arg-parsing.js";
2323
import { openInBrowser } from "../../lib/browser.js";
2424
import { buildCommand } from "../../lib/command.js";
25-
import { ContextError, ResolutionError } from "../../lib/errors.js";
25+
import { ApiError, ContextError, ResolutionError } from "../../lib/errors.js";
2626
import { formatEventDetails } from "../../lib/formatters/index.js";
2727
import { CommandOutput } from "../../lib/formatters/output.js";
2828
import {
@@ -334,6 +334,47 @@ async function fetchLatestEventData(
334334
return { event, trace, spanTreeLines: spanTreeResult?.lines };
335335
}
336336

337+
/**
338+
* Fetch an event, enriching 404 errors with actionable suggestions.
339+
*
340+
* The generic "Failed to get event: 404 Not Found" is the most common
341+
* event view failure (CLI-6F, 54 users). This wrapper adds context about
342+
* data retention, ID format, and cross-project lookup.
343+
*
344+
* @param prefetchedEvent - Already-resolved event (from cross-org lookup), or null
345+
* @param org - Organization slug
346+
* @param project - Project slug
347+
* @param eventId - Event ID being looked up
348+
* @returns The event data
349+
*/
350+
async function fetchEventWithContext(
351+
prefetchedEvent: import("../../types/index.js").SentryEvent | null,
352+
org: string,
353+
project: string,
354+
eventId: string
355+
): Promise<import("../../types/index.js").SentryEvent> {
356+
if (prefetchedEvent) {
357+
return prefetchedEvent;
358+
}
359+
try {
360+
return await getEvent(org, project, eventId);
361+
} catch (error) {
362+
if (error instanceof ApiError && error.status === 404) {
363+
throw new ResolutionError(
364+
`Event '${eventId}'`,
365+
`not found in ${org}/${project}`,
366+
`sentry event view ${org}/${project} <event-id>`,
367+
[
368+
"The event may have been deleted due to data retention policies",
369+
"Verify the event ID is a 32-character hex string (e.g., a1b2c3d4...)",
370+
`Check if the event belongs to a different project: sentry event view ${org}/ <event-id>`,
371+
]
372+
);
373+
}
374+
throw error;
375+
}
376+
}
377+
337378
export const viewCommand = buildCommand({
338379
docs: {
339380
brief: "View details of a specific event",
@@ -425,9 +466,12 @@ export const viewCommand = buildCommand({
425466

426467
// Use the pre-fetched event when cross-project resolution already fetched it,
427468
// avoiding a redundant API call.
428-
const event =
429-
target.prefetchedEvent ??
430-
(await getEvent(target.org, target.project, eventId));
469+
const event = await fetchEventWithContext(
470+
target.prefetchedEvent ?? null,
471+
target.org,
472+
target.project,
473+
eventId
474+
);
431475

432476
// Fetch span tree data (for both JSON and human output)
433477
// Skip when spans=0 (disabled via --spans no or --spans 0)

0 commit comments

Comments
 (0)