Skip to content

Commit 9484444

Browse files
committed
fix(FR-1612): missing session creation error handling (#4456)
Resolves #4454 ([FR-1612](https://lablup.atlassian.net/browse/FR-1612)) # Improve Session Creation Error Handling and Type Safety > [!NOTE] > This PR only handles displaying basic error messages. Improvements to the error messages will be addressed separately. This PR enhances the session creation process by: 1. Adding proper error handling for failed session creations 2. Improving type safety with a dedicated `SessionCreationSuccess` type 3. Implementing the `useErrorMessageResolver` hook to display meaningful error messages 4. Refactoring the session creation promise handling to better group and process results The changes allow users to see specific error messages when session creation fails, while still navigating to the job page when at least one session creation succeeds. ### How to test - Set `allowManualImageNameForSession` to `true` in `config.toml`. - Create a session and specify a manual image name that does not exist (e.g., `123456`). - You can see the error modal as below: ![image.png](https://app.graphite.dev/user-attachments/assets/5c84a153-cd6e-4786-beb6-d579702945f8.png) [FR-1612]: https://lablup.atlassian.net/browse/FR-1612?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
1 parent ad7dc44 commit 9484444

File tree

1 file changed

+76
-49
lines changed

1 file changed

+76
-49
lines changed

react/src/pages/SessionLauncherPage.tsx

Lines changed: 76 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,12 @@ import {
7979
Typography,
8080
theme,
8181
} from 'antd';
82-
import { filterOutEmpty, BAIFlex, toGlobalId } from 'backend.ai-ui';
82+
import {
83+
filterOutEmpty,
84+
BAIFlex,
85+
toGlobalId,
86+
useErrorMessageResolver,
87+
} from 'backend.ai-ui';
8388
import dayjs from 'dayjs';
8489
import { useAtomValue } from 'jotai';
8590
import _ from 'lodash';
@@ -97,6 +102,14 @@ import {
97102
withDefault,
98103
} from 'use-query-params';
99104

105+
// Type for successful session creation result
106+
type SessionCreationSuccess = {
107+
kernelId?: string;
108+
sessionId: string;
109+
sessionName: string;
110+
servicePorts: Array<{ name: string }>;
111+
};
112+
100113
type SessionLauncherFormData = Omit<
101114
Required<OptionalFieldsOnly<SessionLauncherFormValue>>,
102115
'autoMountedFolderNames' | 'mounts'
@@ -203,6 +216,7 @@ const SessionLauncherPage = () => {
203216
const app = App.useApp();
204217

205218
const relayEnv = useRelayEnvironment();
219+
const { getErrorMessage } = useErrorMessageResolver();
206220

207221
const mainContentDivRef = useAtomValue(mainContentDivRefState);
208222
const baiClient = useSuspendedBackendaiClient();
@@ -611,57 +625,70 @@ const SessionLauncherPage = () => {
611625
});
612626

613627
await Promise.allSettled(sessionPromises)
614-
.then(async (sessionCreations) => {
615-
// sessionCreations has failed
616-
if (_.every(sessionCreations, { status: 'rejected' })) {
617-
} else {
618-
// If at least one session creation is successful, navigate to job page and show success notifications
619-
webuiNavigate(redirectTo || '/job');
620-
621-
_.map(sessionCreations, async (creation) => {
622-
if (creation.status === 'fulfilled') {
623-
const session = creation.value as {
624-
kernelId?: string;
625-
sessionId: string;
626-
sessionName: string;
627-
servicePorts: Array<{ name: string }>;
628-
};
629-
const queryResult =
630-
await fetchQuery<SessionLauncherPageAfterCreationQuery>(
631-
relayEnv,
632-
graphql`
633-
query SessionLauncherPageAfterCreationQuery(
634-
$id: GlobalIDField!
635-
) {
636-
compute_session_node(id: $id) {
637-
...BAINodeNotificationItemFragment
638-
}
628+
.then(
629+
async (
630+
sessionCreations: PromiseSettledResult<SessionCreationSuccess>[],
631+
) => {
632+
// Group session creations by their status
633+
const results = _.groupBy(sessionCreations, 'status') as {
634+
fulfilled?: PromiseFulfilledResult<SessionCreationSuccess>[];
635+
rejected?: PromiseRejectedResult[];
636+
};
637+
638+
// Handle successful session creations
639+
_.map(results.fulfilled, async (creation) => {
640+
const session = creation.value;
641+
const queryResult =
642+
await fetchQuery<SessionLauncherPageAfterCreationQuery>(
643+
relayEnv,
644+
graphql`
645+
query SessionLauncherPageAfterCreationQuery(
646+
$id: GlobalIDField!
647+
) {
648+
compute_session_node(id: $id) {
649+
...BAINodeNotificationItemFragment
639650
}
640-
`,
641-
{
642-
id: toGlobalId('ComputeSessionNode', session.sessionId),
643-
},
644-
)
645-
.toPromise()
646-
.catch(() => null);
647-
648-
const createdSession =
649-
queryResult?.compute_session_node ?? null;
650-
651-
if (createdSession) {
652-
upsertNotification({
653-
key: `${SESSION_LAUNCHER_NOTI_PREFIX}${session.sessionId}`,
654-
node: createdSession,
655-
open: true,
656-
duration: 0,
657-
});
658-
}
651+
}
652+
`,
653+
{
654+
id: toGlobalId('ComputeSessionNode', session.sessionId),
655+
},
656+
)
657+
.toPromise()
658+
.catch(() => null);
659+
660+
const createdSession =
661+
queryResult?.compute_session_node ?? null;
662+
663+
if (createdSession) {
664+
upsertNotification({
665+
key: `${SESSION_LAUNCHER_NOTI_PREFIX}${session.sessionId}`,
666+
node: createdSession,
667+
open: true,
668+
duration: 0,
669+
});
659670
}
660671
});
661-
}
662-
})
663-
.catch(() => {
664-
// Handle failed session creations
672+
673+
// If at least one session creation is successful, navigate to job page and show success notifications
674+
if (results.fulfilled && results.fulfilled.length > 0) {
675+
webuiNavigate(redirectTo || '/job');
676+
}
677+
678+
// If there are any failed session creations, show the first error message
679+
if (results.rejected && results.rejected.length > 0) {
680+
const error = results.rejected[0].reason;
681+
app.modal.error({
682+
title: error?.title,
683+
content: getErrorMessage(error),
684+
});
685+
}
686+
},
687+
)
688+
.catch((error) => {
689+
// Unexpected error in `then` of allSettled
690+
console.error('Unexpected error during session creation:', error);
691+
app.message.error(t('error.UnexpectedError'));
665692
});
666693
})
667694
.catch((e) => {

0 commit comments

Comments
 (0)