@@ -27,6 +27,7 @@ import { ApiError, ContextError, ResolutionError } from "../../lib/errors.js";
2727import { formatEventDetails } from "../../lib/formatters/index.js" ;
2828import { filterFields } from "../../lib/formatters/json.js" ;
2929import { CommandOutput } from "../../lib/formatters/output.js" ;
30+ import { HEX_ID_RE , normalizeHexId } from "../../lib/hex-id.js" ;
3031import {
3132 applyFreshFlag ,
3233 FRESH_ALIASES ,
@@ -114,20 +115,39 @@ const USAGE_HINT = "sentry event view <org>/<project> <event-id>";
114115 * "org/project" with a missing event ID.
115116 */
116117function parseSingleArg ( arg : string ) : ParsedPositionalArgs {
117- // Detect "org/SHORT-ID" pattern before parseSlashSeparatedArg.
118- // e.g., "figma/FULLSCREEN-2RN" → auto-redirect to that issue's latest event .
118+ // Detect "org/SHORT-ID" and "SHORT-ID/EVENT-ID" patterns before
119+ // parseSlashSeparatedArg, which throws ContextError for single-slash args .
119120 const slashIdx = arg . indexOf ( "/" ) ;
120121 if ( slashIdx !== - 1 && arg . indexOf ( "/" , slashIdx + 1 ) === - 1 ) {
122+ const beforeSlash = arg . slice ( 0 , slashIdx ) ;
121123 const afterSlash = arg . slice ( slashIdx + 1 ) ;
124+
125+ // "org/SHORT-ID" → auto-redirect to that issue's latest event.
126+ // e.g., "figma/FULLSCREEN-2RN"
122127 if ( afterSlash && looksLikeIssueShortId ( afterSlash ) ) {
123128 // Use "org/" (trailing slash) to signal OrgAll mode so downstream
124129 // parseOrgProjectArg interprets this as an org, not a project search.
125130 return {
126131 eventId : "latest" ,
127- targetArg : `${ arg . slice ( 0 , slashIdx ) } /` ,
132+ targetArg : `${ beforeSlash } /` ,
128133 issueShortId : afterSlash ,
129134 } ;
130135 }
136+
137+ // "SHORT-ID/EVENT-ID" → view a specific event identified by issue short ID.
138+ // e.g., "CLI-G5/abc123def456abc123def456abc123de"
139+ if (
140+ beforeSlash &&
141+ looksLikeIssueShortId ( beforeSlash ) &&
142+ afterSlash &&
143+ HEX_ID_RE . test ( normalizeHexId ( afterSlash ) )
144+ ) {
145+ return {
146+ eventId : normalizeHexId ( afterSlash ) ,
147+ targetArg : undefined ,
148+ issueShortId : beforeSlash ,
149+ } ;
150+ }
131151 }
132152
133153 const { id : eventId , targetArg } = parseSlashSeparatedArg (
@@ -497,6 +517,26 @@ async function resolveIssueShortIdEvent(
497517 return fetchLatestEventData ( org , issue . id , spans ) ;
498518}
499519
520+ /**
521+ * Fetch a specific event by ID (not latest) and build full EventViewData
522+ * including optional span tree. Used by the SHORT-ID/EVENT-ID path.
523+ */
524+ async function fetchSpecificEventData (
525+ org : string ,
526+ project : string ,
527+ eventId : string ,
528+ spans : number
529+ ) : Promise < EventViewData > {
530+ const event = await getEvent ( org , project , eventId ) ;
531+ const spanTreeResult =
532+ spans > 0 ? await getSpanTreeLines ( org , event , spans ) : undefined ;
533+ const trace =
534+ spanTreeResult ?. success && spanTreeResult . traceId
535+ ? { traceId : spanTreeResult . traceId , spans : spanTreeResult . spans ?? [ ] }
536+ : null ;
537+ return { event, trace, spanTreeLines : spanTreeResult ?. lines } ;
538+ }
539+
500540/** Result from an issue-based shortcut (URL or short ID) */
501541type IssueShortcutResult = {
502542 org : string ;
@@ -507,6 +547,7 @@ type IssueShortcutResult = {
507547/** Options for resolving issue-based shortcuts */
508548type IssueShortcutOptions = {
509549 parsed : ReturnType < typeof parseOrgProjectArg > ;
550+ eventId : string ;
510551 issueId : string | undefined ;
511552 issueShortId : string | undefined ;
512553 cwd : string ;
@@ -524,7 +565,7 @@ type IssueShortcutOptions = {
524565async function resolveIssueShortcut (
525566 options : IssueShortcutOptions
526567) : Promise < IssueShortcutResult | null > {
527- const { parsed, issueId, issueShortId, cwd, spans } = options ;
568+ const { parsed, eventId , issueId, issueShortId, cwd, spans } = options ;
528569 const log = logger . withTag ( "event.view" ) ;
529570
530571 // Issue URL shortcut: fetch the latest event directly via the issue ID.
@@ -540,13 +581,9 @@ async function resolveIssueShortcut(
540581 }
541582
542583 // Issue short ID auto-redirect: user passed an issue short ID
543- // (e.g., "BRUNCHIE-APP-29" or "figma/FULLSCREEN-2RN ") instead of a hex
544- // event ID. Resolve the issue and show its latest event .
584+ // (e.g., "BRUNCHIE-APP-29" or "CLI-G5/abc123... ") instead of or
585+ // alongside a hex event ID. Resolve the issue to get org/project .
545586 if ( issueShortId ) {
546- log . warn (
547- `'${ issueShortId } ' is an issue short ID, not an event ID. Showing the latest event.`
548- ) ;
549-
550587 // Use the explicit org from the parsed target if available (e.g.,
551588 // "figma/" → org-all with org "figma"), otherwise fall back to
552589 // auto-detection via DSN/env/config.
@@ -559,6 +596,35 @@ async function resolveIssueShortcut(
559596 ) ;
560597 }
561598
599+ // When the user specified a specific event ID (SHORT-ID/EVENT-ID),
600+ // resolve the issue to get the project, then fetch the specific event.
601+ if ( eventId !== "latest" ) {
602+ const issue = await getIssueByShortId ( resolved . org , issueShortId ) ;
603+ const issueProject = issue . project ?. slug ;
604+ if ( ! issueProject ) {
605+ throw new ResolutionError (
606+ `Issue '${ issueShortId } '` ,
607+ "has no associated project" ,
608+ `sentry event view <org>/<project> ${ eventId } ` ,
609+ [ "Specify the project explicitly to view this event" ]
610+ ) ;
611+ }
612+ const data = await fetchSpecificEventData (
613+ resolved . org ,
614+ issueProject ,
615+ eventId ,
616+ spans
617+ ) ;
618+ return {
619+ org : resolved . org ,
620+ data,
621+ hint : `Viewing event ${ eventId } for issue ${ issueShortId } ` ,
622+ } ;
623+ }
624+
625+ log . warn (
626+ `'${ issueShortId } ' is an issue short ID, not an event ID. Showing the latest event.`
627+ ) ;
562628 const data = await resolveIssueShortIdEvent (
563629 issueShortId ,
564630 resolved . org ,
@@ -624,9 +690,11 @@ export const viewCommand = buildCommand({
624690 const parsed = parseOrgProjectArg ( targetArg ) ;
625691
626692 // Handle issue-based shortcuts (issue URLs and short IDs) before
627- // normal event resolution. Both paths fetch the latest event.
693+ // normal event resolution. When eventId is "latest", fetches the
694+ // latest event; otherwise fetches the specific event.
628695 const issueShortcut = await resolveIssueShortcut ( {
629696 parsed,
697+ eventId,
630698 issueId,
631699 issueShortId,
632700 cwd,
0 commit comments