diff --git a/static/app/components/stackTrace/issueStackTrace/index.spec.tsx b/static/app/components/stackTrace/issueStackTrace/index.spec.tsx index 718dbbbb785f..ccd17952dc97 100644 --- a/static/app/components/stackTrace/issueStackTrace/index.spec.tsx +++ b/static/app/components/stackTrace/issueStackTrace/index.spec.tsx @@ -118,6 +118,36 @@ describe('IssueStackTrace', () => { expect(container).toBeEmptyDOMElement(); }); + it('renders exception details when no structured stacktrace is available', async () => { + const event = EventFixture({ + platform: 'python', + projectID: '1', + entries: [{type: 'exception' as const, data: {values: []}}], + }); + + render( + + ); + + expect(await screen.findByText('Stack Trace')).toBeInTheDocument(); + expect(screen.getByText('ValueError')).toBeInTheDocument(); + expect(screen.getByText('bad value')).toBeInTheDocument(); + expect(screen.getByText('No stacktrace found.')).toBeInTheDocument(); + }); + it('persists raw and minified display selections per project', async () => { const {event, stacktrace} = makeStackTraceData(); const minifiedStacktrace = { diff --git a/static/app/components/stackTrace/issueStackTrace/index.tsx b/static/app/components/stackTrace/issueStackTrace/index.tsx index a56026a22b53..31f7ac315cb5 100644 --- a/static/app/components/stackTrace/issueStackTrace/index.tsx +++ b/static/app/components/stackTrace/issueStackTrace/index.tsx @@ -263,9 +263,11 @@ function IssueStackTraceContent({ )} - - - + {exc.stacktrace && ( + + + + )} - {idx === firstVisibleExceptionIndex ? ( + {exc.stacktrace && idx === firstVisibleExceptionIndex ? ( diff --git a/static/app/components/stackTrace/issueStackTrace/utils.ts b/static/app/components/stackTrace/issueStackTrace/utils.ts index 02d1bbbfe0a9..90d6cd5285b2 100644 --- a/static/app/components/stackTrace/issueStackTrace/utils.ts +++ b/static/app/components/stackTrace/issueStackTrace/utils.ts @@ -2,11 +2,9 @@ import {displayRawContent as rawStacktraceContent} from 'sentry/components/event import type {StackTraceView} from 'sentry/components/stackTrace/types'; import type {Event, ExceptionValue} from 'sentry/types/event'; import {EntryType} from 'sentry/types/event'; -import type {StacktraceType} from 'sentry/types/stacktrace'; interface IndexedExceptionValue extends ExceptionValue { exceptionIndex: number; - stacktrace: StacktraceType; } /** @@ -28,9 +26,7 @@ export function getOrderedExceptions( isNewestFirst: boolean, view: StackTraceView ): IndexedExceptionValue[] { - const indexed = values - .map((exc, exceptionIndex) => ({...exc, exceptionIndex})) - .filter((exc): exc is IndexedExceptionValue => exc.stacktrace !== null); + const indexed = values.map((exc, exceptionIndex) => ({...exc, exceptionIndex})); return isNewestFirst && view !== 'raw' ? indexed.reverse() : indexed; } diff --git a/static/app/components/stackTrace/stackTraceProvider.tsx b/static/app/components/stackTrace/stackTraceProvider.tsx index 0724a687c381..bd285a305b21 100644 --- a/static/app/components/stackTrace/stackTraceProvider.tsx +++ b/static/app/components/stackTrace/stackTraceProvider.tsx @@ -1,6 +1,7 @@ import {useCallback, useMemo, useState} from 'react'; import {isExpandable as frameHasExpandableDetails} from 'sentry/components/events/interfaces/frame/utils'; +import {NoStackTraceMessage} from 'sentry/components/events/interfaces/noStackTraceMessage'; import {getLastFrameIndex} from 'sentry/components/events/interfaces/utils'; import type {Event} from 'sentry/types/event'; import type {PlatformKey} from 'sentry/types/platform'; @@ -12,6 +13,13 @@ import {StackTraceContext, useStackTraceViewState} from './stackTraceContext'; import type {StackTraceContextValue} from './stackTraceContext'; import type {StackTraceProviderProps} from './types'; +const EMPTY_STACKTRACE: StacktraceType = { + frames: [], + framesOmitted: null, + hasSystemFrames: false, + registers: null, +}; + function getDefaultPlatform(stacktrace: StacktraceType, event: Event): PlatformKey { const framePlatform = stacktrace.frames?.find(frame => !!frame.platform)?.platform; return event.platform ?? framePlatform ?? 'other'; @@ -33,8 +41,10 @@ export function StackTraceProvider({ }: StackTraceProviderProps) { const {isMinified, isNewestFirst, view} = useStackTraceViewState(); - const activeStacktrace = - isMinified && minifiedStacktrace ? minifiedStacktrace : stacktrace; + let activeStacktrace = stacktrace ?? EMPTY_STACKTRACE; + if (isMinified && minifiedStacktrace) { + activeStacktrace = minifiedStacktrace; + } const frames = useMemo(() => activeStacktrace.frames ?? [], [activeStacktrace.frames]); const {projects} = useProjects(); const project = useMemo( @@ -161,6 +171,10 @@ export function StackTraceProvider({ ] ); + if (!stacktrace) { + return ; + } + return ( {children} ); diff --git a/static/app/components/stackTrace/types.tsx b/static/app/components/stackTrace/types.tsx index 7d0f5c0c63f3..173e4920e0d9 100644 --- a/static/app/components/stackTrace/types.tsx +++ b/static/app/components/stackTrace/types.tsx @@ -55,7 +55,7 @@ export type StackTraceMeta = { export interface StackTraceProviderProps { children: ReactNode; event: Event; - stacktrace: StacktraceType; + stacktrace: StacktraceType | null; /** When true, all frames start collapsed regardless of their position. */ collapseAll?: boolean; /** Optional exception index in the full exception values list. */