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
30 changes: 30 additions & 0 deletions static/app/components/stackTrace/issueStackTrace/index.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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(
<IssueStackTrace
event={event}
values={[
{
type: 'ValueError',
value: 'bad value',
module: null,
mechanism: null,
stacktrace: null,
rawStacktrace: null,
threadId: null,
},
]}
/>
);

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 = {
Expand Down
10 changes: 6 additions & 4 deletions static/app/components/stackTrace/issueStackTrace/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -263,9 +263,11 @@ function IssueStackTraceContent({
</Fragment>
)}
</Stack>
<ErrorBoundary customComponent={null}>
<StacktraceBanners event={event} stacktrace={exc.stacktrace} />
</ErrorBoundary>
{exc.stacktrace && (
<ErrorBoundary customComponent={null}>
<StacktraceBanners event={event} stacktrace={exc.stacktrace} />
</ErrorBoundary>
)}
<StackTraceProvider
exceptionIndex={isStandalone ? undefined : exc.exceptionIndex}
event={event}
Expand Down Expand Up @@ -347,7 +349,7 @@ function IssueStackTraceContent({
newestFirst={isNewestFirst}
onExceptionClick={expandException}
/>
{idx === firstVisibleExceptionIndex ? (
{exc.stacktrace && idx === firstVisibleExceptionIndex ? (
<ErrorBoundary customComponent={null}>
<StacktraceBanners event={event} stacktrace={exc.stacktrace} />
</ErrorBoundary>
Expand Down
6 changes: 1 addition & 5 deletions static/app/components/stackTrace/issueStackTrace/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

/**
Expand All @@ -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;
}

Expand Down
18 changes: 16 additions & 2 deletions static/app/components/stackTrace/stackTraceProvider.tsx
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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';
Expand All @@ -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(
Expand Down Expand Up @@ -161,6 +171,10 @@ export function StackTraceProvider({
]
);

if (!stacktrace) {
return <NoStackTraceMessage />;
}

return (
<StackTraceContext.Provider value={value}>{children}</StackTraceContext.Provider>
);
Expand Down
2 changes: 1 addition & 1 deletion static/app/components/stackTrace/types.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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. */
Expand Down
Loading