Skip to content

Commit 808fb10

Browse files
committed
feat(FR-2898): improve SFTP session creation error feedback
1 parent f4e2e10 commit 808fb10

24 files changed

Lines changed: 110 additions & 78 deletions

packages/backend.ai-ui/src/hooks/useErrorMessageResolver.ts

Lines changed: 42 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,22 +26,48 @@ export type ESMClientErrorResponse = {
2626
response?: ErrorResponse;
2727
};
2828

29+
type ErrorLike = Partial<ErrorResponse & Error & ESMClientErrorResponse>;
30+
31+
export type GetErrorMessageOptions = {
32+
defaultMessage?: string;
33+
/**
34+
* 'normal' (default) returns just the human-readable message, appending
35+
* `(error_code)` when present — suitable for end-user surfaces.
36+
*
37+
* 'detail' additionally prefixes the HTTP `statusCode` and `error_code`
38+
* in a structured form, e.g. `[HTTP 500] [BAI_E0001] message text`.
39+
* Use this on operator-facing failure surfaces (e.g. SFTP session
40+
* creation) where classifying the failure (4xx policy/quota vs 5xx
41+
* agent/storage) matters more than a clean copy.
42+
*/
43+
verbosity?: 'normal' | 'detail';
44+
};
45+
2946
const useErrorMessageResolver = () => {
3047
const { t } = useTranslation();
3148

32-
const isErrorLike = (
33-
error: unknown,
34-
): error is Partial<ErrorResponse & Error> => {
49+
const isErrorLike = (error: unknown): error is ErrorLike => {
3550
return typeof error === 'object' && error !== null;
3651
};
3752

3853
/**
3954
* Resolves the error message for a given error object.
4055
* @param error - The error object to resolve.
41-
* @param defaultMessage - (optional) The default message to return if no specific message is found.
56+
* @param defaultMessageOrOptions - Either a default fallback string
57+
* (backwards compatible) or an options object with `defaultMessage`
58+
* and `verbosity`.
4259
* @returns - The resolved error message (string).
4360
*/
44-
const getErrorMessage = (error: unknown, defaultMessage?: string): string => {
61+
const getErrorMessage = (
62+
error: unknown,
63+
defaultMessageOrOptions?: string | GetErrorMessageOptions,
64+
): string => {
65+
const options: GetErrorMessageOptions =
66+
typeof defaultMessageOrOptions === 'string'
67+
? { defaultMessage: defaultMessageOrOptions }
68+
: (defaultMessageOrOptions ?? {});
69+
const { defaultMessage, verbosity = 'normal' } = options;
70+
4571
let errorMsg = defaultMessage || t('error.UnknownError');
4672
if (!error || !isErrorLike(error)) return errorMsg;
4773

@@ -54,6 +80,17 @@ const useErrorMessageResolver = () => {
5480
} else if (error.title) {
5581
errorMsg = error.title;
5682
}
83+
84+
if (verbosity === 'detail') {
85+
const prefixes = _.compact([
86+
_.isNumber(error.statusCode) ? `[HTTP ${error.statusCode}]` : null,
87+
error.error_code ? `[${error.error_code}]` : null,
88+
]);
89+
return prefixes.length > 0
90+
? `${prefixes.join(' ')} ${errorMsg}`
91+
: errorMsg;
92+
}
93+
5794
if (error.error_code) {
5895
errorMsg = _.join([errorMsg, `(${error.error_code})`], ' ');
5996
}

react/src/components/SFTPServerButton.tsx

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ import {
1818
StartSessionWithDefaultValue,
1919
useStartSession,
2020
} from '../hooks/useStartSession';
21-
import { EllipsisOutlined } from '@ant-design/icons';
22-
import { App, Dropdown, Image, Space, Tooltip } from 'antd';
21+
import { CloseCircleOutlined, EllipsisOutlined } from '@ant-design/icons';
22+
import { App, Dropdown, Image, Space, Tooltip, Typography, theme } from 'antd';
2323
import {
2424
BAIButton,
2525
BAIButtonProps,
@@ -46,6 +46,7 @@ const SFTPServerButton: React.FC<SFTPServerButtonProps> = ({
4646
const { logger } = useBAILogger();
4747
const { t } = useTranslation();
4848
const { message, modal } = App.useApp();
49+
const { token } = theme.useToken();
4950

5051
const webuiNavigate = useWebUINavigate();
5152

@@ -159,9 +160,34 @@ const SFTPServerButton: React.FC<SFTPServerButtonProps> = ({
159160
}
160161
if (results?.rejected && results.rejected.length > 0) {
161162
const error = results.rejected[0].reason;
162-
modal.error({
163-
title: error?.title,
164-
content: getErrorMessage(error),
163+
modal.confirm({
164+
icon: (
165+
<CloseCircleOutlined
166+
style={{ color: token.colorError }}
167+
/>
168+
),
169+
title: t('data.explorer.SFTPSessionCreationFailed'),
170+
content: (
171+
<Typography.Paragraph
172+
style={{ marginTop: token.marginSM, marginBottom: 0 }}
173+
>
174+
<Typography.Text>
175+
{getErrorMessage(error, { verbosity: 'detail' })}
176+
</Typography.Text>
177+
<br />
178+
<Typography.Text type="secondary">
179+
{t('data.explorer.SFTPSessionFailureHint')}
180+
</Typography.Text>
181+
</Typography.Paragraph>
182+
),
183+
okText: t('session.GoToUploadSessionList'),
184+
cancelText: t('button.Close'),
185+
onOk: () => {
186+
webuiNavigate({
187+
pathname: '/session',
188+
search: '?type=system',
189+
});
190+
},
165191
});
166192
}
167193
})

react/src/components/SFTPServerButtonV2.tsx

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ import {
1818
StartSessionWithDefaultValue,
1919
useStartSession,
2020
} from '../hooks/useStartSession';
21-
import { EllipsisOutlined } from '@ant-design/icons';
22-
import { App, Dropdown, Image, Space, Tooltip } from 'antd';
21+
import { CloseCircleOutlined, EllipsisOutlined } from '@ant-design/icons';
22+
import { App, Dropdown, Image, Space, Tooltip, Typography, theme } from 'antd';
2323
import {
2424
BAIButton,
2525
BAIButtonProps,
@@ -46,6 +46,7 @@ const SFTPServerButtonV2: React.FC<SFTPServerButtonV2Props> = ({
4646
const { logger } = useBAILogger();
4747
const { t } = useTranslation();
4848
const { message, modal } = App.useApp();
49+
const { token } = theme.useToken();
4950

5051
const webuiNavigate = useWebUINavigate();
5152

@@ -159,9 +160,34 @@ const SFTPServerButtonV2: React.FC<SFTPServerButtonV2Props> = ({
159160
}
160161
if (results?.rejected && results.rejected.length > 0) {
161162
const error = results.rejected[0].reason;
162-
modal.error({
163-
title: error?.title,
164-
content: getErrorMessage(error),
163+
modal.confirm({
164+
icon: (
165+
<CloseCircleOutlined
166+
style={{ color: token.colorError }}
167+
/>
168+
),
169+
title: t('data.explorer.SFTPSessionCreationFailed'),
170+
content: (
171+
<Typography.Paragraph
172+
style={{ marginTop: token.marginSM, marginBottom: 0 }}
173+
>
174+
<Typography.Text>
175+
{getErrorMessage(error, { verbosity: 'detail' })}
176+
</Typography.Text>
177+
<br />
178+
<Typography.Text type="secondary">
179+
{t('data.explorer.SFTPSessionFailureHint')}
180+
</Typography.Text>
181+
</Typography.Paragraph>
182+
),
183+
okText: t('session.GoToUploadSessionList'),
184+
cancelText: t('button.Close'),
185+
onOk: () => {
186+
webuiNavigate({
187+
pathname: '/session',
188+
search: '?type=system',
189+
});
190+
},
165191
});
166192
}
167193
})

resources/i18n/de.json

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -701,8 +701,6 @@
701701
"NoSharedFolders": "Keine freigegebenen Ordner",
702702
"NoSharedUsers": "Niemand wurde eingeladen",
703703
"NotEnoughResourceForFileBrowserSession": "Nicht genügend Ressourcen (CPU: 1 Core, Speicher: 0,5 GB), um die Sitzung für den Dateibrowser zu erstellen. Bitte überprüfen Sie die verfügbaren Ressourcen.",
704-
"NumberOfSFTPSessionsExceededBody": "Sie führen alle verfügbaren Upload-Sitzungen aus, die Sie erstellen können. Bitte beenden Sie ungenutzte Upload-Sitzungen, bevor Sie eine neue Sitzung starten.",
705-
"NumberOfSFTPSessionsExceededTitle": "Limit der laufenden Upload-Session erreicht",
706704
"People": "Menschen",
707705
"Permission": "Genehmigung",
708706
"Permissions": "Berechtigungen",
@@ -713,7 +711,6 @@
713711
"RenameAFolder": "Ordner umbenennen",
714712
"RetryingOperation": "Wiederholung, um eine Sitzung festzulegen ...",
715713
"RunSSH/SFTPserver": "SFTP-Server starten",
716-
"SFTPSessionNotAvailable": "SFTP Session ist jetzt nicht verfügbar",
717714
"ShareFolder": "Ordner teilen",
718715
"Size": "Größe",
719716
"StartingSSH/SFTPSession": "SFTP-Sitzung starten...",

resources/i18n/el.json

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -701,8 +701,6 @@
701701
"NoSharedFolders": "Δεν υπάρχουν κοινόχρηστοι φάκελοι",
702702
"NoSharedUsers": "Κανείς δεν έχει προσκληθεί",
703703
"NotEnoughResourceForFileBrowserSession": "Δεν υπάρχουν αρκετοί πόροι (cpu: 1 Core, mem: 0,5 GB) για τη δημιουργία της περιόδου λειτουργίας για το πρόγραμμα περιήγησης αρχείων παρακαλούμε ελέγξτε τους διαθέσιμους πόρους.",
704-
"NumberOfSFTPSessionsExceededBody": "Εκτελείτε όλες τις διαθέσιμες συνεδρίες μεταφόρτωσης που επιτρέπεται να δημιουργήσετε. Παρακαλούμε τερματίστε τις αχρησιμοποίητες συνεδρίες μεταφόρτωσης πριν ξεκινήσετε μια νέα συνεδρία.",
705-
"NumberOfSFTPSessionsExceededTitle": "Έφθασε το όριο του αριθμού των τρεχουσών συνόδων μεταφόρτωσης",
706704
"People": "Ανθρωποι",
707705
"Permission": "Αδεια",
708706
"Permissions": "Άδειες",
@@ -713,7 +711,6 @@
713711
"RenameAFolder": "Μετονομασία φακέλου",
714712
"RetryingOperation": "Επανένταξη για την καθιέρωση της συνεδρίας ...",
715713
"RunSSH/SFTPserver": "Εκτέλεση διακομιστή SFTP",
716-
"SFTPSessionNotAvailable": "Το SFTP Session δεν είναι διαθέσιμο τώρα",
717714
"ShareFolder": "Κοινόχρηστο φάκελο",
718715
"Size": "Μέγεθος",
719716
"StartingSSH/SFTPSession": "Έναρξη συνεδρίας SFTP...",

resources/i18n/en.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -719,8 +719,6 @@
719719
"NoSharedFolders": "No shared folders",
720720
"NoSharedUsers": "No one has been invited",
721721
"NotEnoughResourceForFileBrowserSession": "No enough resources(cpu: 1 Core, mem: 0.5GB) to create the session for filebrowser. please check the available resources.",
722-
"NumberOfSFTPSessionsExceededBody": "You are running all available upload sessions you are allowed to create. Please terminated unused upload sessions before starting a new session.",
723-
"NumberOfSFTPSessionsExceededTitle": "Reached limit of running upload session count",
724722
"People": "People",
725723
"Permission": "Permission",
726724
"Permissions": "Permissions",
@@ -731,7 +729,8 @@
731729
"RenameAFolder": "Rename Folder",
732730
"RetryingOperation": "Retrying to establish session...",
733731
"RunSSH/SFTPserver": "Run SFTP server",
734-
"SFTPSessionNotAvailable": "SFTP Session is not available now",
732+
"SFTPSessionCreationFailed": "Failed to create SFTP session",
733+
"SFTPSessionFailureHint": "If many upload sessions are still running, terminating unused ones may free resources on the SFTP host.",
735734
"ShareFolder": "Share Folder",
736735
"Size": "Size",
737736
"StartingSSH/SFTPSession": "Starting SFTP session...",
@@ -2571,6 +2570,7 @@
25712570
"GPU": "GPU",
25722571
"GPU(MEM)": "GPU(Memory)",
25732572
"Gaudi2Enabled": "Gaudi 2 NPU Enabled",
2573+
"GoToUploadSessionList": "Go to upload sessions",
25742574
"GracePeriod": "Grace Period",
25752575
"GracePeriodDesc": "Utilization idle checker will be activated after this initial grace time. During this time, sessions are not terminated even if utilization is low.",
25762576
"Host": "Host",

resources/i18n/es.json

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -701,8 +701,6 @@
701701
"NoSharedFolders": "Sin carpetas compartidas",
702702
"NoSharedUsers": "No se ha invitado a nadie.",
703703
"NotEnoughResourceForFileBrowserSession": "No hay recursos suficientes (cpu: 1 Core, mem: 0.5GB) para crear la sesión para filebrowser. por favor compruebe los recursos disponibles.",
704-
"NumberOfSFTPSessionsExceededBody": "Está ejecutando todas las sesiones de carga disponibles que puede crear. Elimine las sesiones de carga no utilizadas antes de iniciar una nueva sesión.",
705-
"NumberOfSFTPSessionsExceededTitle": "Se ha alcanzado el límite del recuento de sesiones de carga en ejecución",
706704
"People": "Personas",
707705
"Permission": "Permiso",
708706
"Permissions": "Permisos",
@@ -713,7 +711,6 @@
713711
"RenameAFolder": "Renombrar carpeta",
714712
"RetryingOperation": "Vuelva a intentar establecer sesión ...",
715713
"RunSSH/SFTPserver": "Ejecutar servidor SFTP",
716-
"SFTPSessionNotAvailable": "SFTP Session no está disponible ahora",
717714
"ShareFolder": "Compartir carpeta",
718715
"Size": "Talla",
719716
"StartingSSH/SFTPSession": "Iniciando sesión SFTP...",

resources/i18n/fi.json

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -701,8 +701,6 @@
701701
"NoSharedFolders": "Ei jaettuja kansioita",
702702
"NoSharedUsers": "Ketään ei ole kutsuttu.",
703703
"NotEnoughResourceForFileBrowserSession": "Resurssit eivät riitä (cpu: 1 Core, mem: 0.5GB) filebrowser-istunnon luomiseen. tarkista käytettävissä olevat resurssit.",
704-
"NumberOfSFTPSessionsExceededBody": "Käytössäsi ovat kaikki käytettävissä olevat latausistunnot, joita voit luoda. Lopeta käyttämättömät latausistunnot ennen uuden istunnon aloittamista.",
705-
"NumberOfSFTPSessionsExceededTitle": "Käynnissä olevan latausistunnon lukumäärän raja saavutettu",
706704
"People": "Ihmiset",
707705
"Permission": "Lupa",
708706
"Permissions": "Luvat",
@@ -713,7 +711,6 @@
713711
"RenameAFolder": "Nimeä kansio uudelleen",
714712
"RetryingOperation": "Uudelleenjärjestelmä istunnon perustamiseksi ...",
715713
"RunSSH/SFTPserver": "Käynnistä SFTP-palvelin",
716-
"SFTPSessionNotAvailable": "SFTP-istunto ei ole nyt käytettävissä",
717714
"ShareFolder": "Jaa kansio",
718715
"Size": "Koko",
719716
"StartingSSH/SFTPSession": "SFTP-istunnon käynnistäminen...",

resources/i18n/fr.json

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -701,8 +701,6 @@
701701
"NoSharedFolders": "Aucun dossier partagé",
702702
"NoSharedUsers": "Personne n'a été invité.",
703703
"NotEnoughResourceForFileBrowserSession": "Pas assez de ressources (cpu : 1 Core, mem : 0,5 Go) pour créer la session pour le navigateur de fichiers. veuillez vérifier les ressources disponibles.",
704-
"NumberOfSFTPSessionsExceededBody": "Vous exécutez toutes les sessions de téléchargement que vous êtes autorisé à créer. Veuillez mettre fin aux sessions de téléchargement inutilisées avant de commencer une nouvelle session.",
705-
"NumberOfSFTPSessionsExceededTitle": "Limite atteinte pour le nombre de sessions de téléchargement en cours",
706704
"People": "Gens",
707705
"Permission": "Autorisation",
708706
"Permissions": "Autorisations",
@@ -713,7 +711,6 @@
713711
"RenameAFolder": "Renommer le dossier",
714712
"RetryingOperation": "Réessayant pour établir la session ...",
715713
"RunSSH/SFTPserver": "Exécuter le serveur SFTP",
716-
"SFTPSessionNotAvailable": "SFTP Session n'est pas disponible pour le moment",
717714
"ShareFolder": "Dossier de partage",
718715
"Size": "Taille",
719716
"StartingSSH/SFTPSession": "Démarrage de la session SFTP...",

resources/i18n/id.json

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -701,8 +701,6 @@
701701
"NoSharedFolders": "Tidak ada folder bersama",
702702
"NoSharedUsers": "Tidak ada yang diundang",
703703
"NotEnoughResourceForFileBrowserSession": "Tidak ada sumber daya yang cukup (cpu: 1 Core, mem: 0.5GB) untuk membuat sesi untuk filebrowser. Mohon periksa sumber daya yang tersedia.",
704-
"NumberOfSFTPSessionsExceededBody": "Anda menjalankan semua sesi unggahan yang tersedia yang diizinkan untuk Anda buat. Hentikan sesi unggahan yang tidak terpakai sebelum memulai sesi baru.",
705-
"NumberOfSFTPSessionsExceededTitle": "Mencapai batas jumlah sesi unggahan yang sedang berjalan",
706704
"People": "Orang-orang",
707705
"Permission": "Izin",
708706
"Permissions": "Perizinan",
@@ -713,7 +711,6 @@
713711
"RenameAFolder": "Mengganti nama folder",
714712
"RetryingOperation": "Mencoba lagi untuk menetapkan sesi ...",
715713
"RunSSH/SFTPserver": "Jalankan server SFTP",
716-
"SFTPSessionNotAvailable": "Sesi SFTP tidak tersedia sekarang",
717714
"ShareFolder": "Bagikan Folder",
718715
"Size": "Ukuran",
719716
"StartingSSH/SFTPSession": "Memulai sesi SFTP...",

0 commit comments

Comments
 (0)