diff --git a/browser_tests/tests/dialog.spec.ts b/browser_tests/tests/dialog.spec.ts
index 558dff2068..bd8daec179 100644
--- a/browser_tests/tests/dialog.spec.ts
+++ b/browser_tests/tests/dialog.spec.ts
@@ -309,3 +309,21 @@ test.describe('Feedback dialog', () => {
await expect(feedbackHeader).not.toBeVisible()
})
})
+
+test.describe('Error dialog', () => {
+ test('Should display an error dialog when graph configure fails', async ({
+ comfyPage
+ }) => {
+ await comfyPage.page.evaluate(() => {
+ const graph = window['graph']
+ graph.configure = () => {
+ throw new Error('Error on configure!')
+ }
+ })
+
+ await comfyPage.loadWorkflow('default')
+
+ const errorDialog = comfyPage.page.locator('.error-dialog-content')
+ await expect(errorDialog).toBeVisible()
+ })
+})
diff --git a/src/components/dialog/content/ErrorDialogContent.vue b/src/components/dialog/content/ErrorDialogContent.vue
new file mode 100644
index 0000000000..a1cb4113f3
--- /dev/null
+++ b/src/components/dialog/content/ErrorDialogContent.vue
@@ -0,0 +1,80 @@
+
+
+
+
+ {{ stackTrace }}
+
+
+
+ {{ t('errorDialog.extensionFileHint') }}:
+
+ {{ extensionFile }}
+
+
+
+
+
+
+
+
+
diff --git a/src/locales/en/main.json b/src/locales/en/main.json
index fd3acf1472..3c8a26d6be 100644
--- a/src/locales/en/main.json
+++ b/src/locales/en/main.json
@@ -951,6 +951,12 @@
"missingModels": "Missing Models",
"missingModelsMessage": "When loading the graph, the following models were not found"
},
+ "errorDialog": {
+ "defaultTitle": "An error occurred",
+ "loadWorkflowTitle": "Loading aborted due to error reloading workflow data",
+ "noStackTrace": "No stacktrace available",
+ "extensionFileHint": "This may be due to the following script"
+ },
"desktopUpdate": {
"title": "Updating ComfyUI Desktop",
"description": "ComfyUI Desktop is installing new dependencies. This may take a few minutes.",
diff --git a/src/locales/fr/main.json b/src/locales/fr/main.json
index b94b9c3322..a50efa80c1 100644
--- a/src/locales/fr/main.json
+++ b/src/locales/fr/main.json
@@ -115,6 +115,12 @@
"paused": "En pause",
"resume": "Reprendre le téléchargement"
},
+ "errorDialog": {
+ "defaultTitle": "Une erreur est survenue",
+ "extensionFileHint": "Cela peut être dû au script suivant",
+ "loadWorkflowTitle": "Chargement interrompu en raison d'une erreur de rechargement des données de workflow",
+ "noStackTrace": "Aucune trace de pile disponible"
+ },
"g": {
"about": "À propos",
"add": "Ajouter",
diff --git a/src/locales/ja/main.json b/src/locales/ja/main.json
index 2b0b0ff83c..8a22093f1a 100644
--- a/src/locales/ja/main.json
+++ b/src/locales/ja/main.json
@@ -115,6 +115,12 @@
"paused": "一時停止",
"resume": "ダウンロードを再開"
},
+ "errorDialog": {
+ "defaultTitle": "エラーが発生しました",
+ "extensionFileHint": "これは次のスクリプトが原因かもしれません",
+ "loadWorkflowTitle": "ワークフローデータの再読み込みエラーにより、読み込みが中止されました",
+ "noStackTrace": "スタックトレースは利用できません"
+ },
"g": {
"about": "情報",
"add": "追加",
diff --git a/src/locales/ko/main.json b/src/locales/ko/main.json
index ff588b77a0..3abee4320c 100644
--- a/src/locales/ko/main.json
+++ b/src/locales/ko/main.json
@@ -115,6 +115,12 @@
"paused": "일시 중지됨",
"resume": "다운로드 재개"
},
+ "errorDialog": {
+ "defaultTitle": "오류가 발생했습니다",
+ "extensionFileHint": "다음 스크립트 때문일 수 있습니다",
+ "loadWorkflowTitle": "워크플로우 데이터를 다시 로드하는 중 오류로 인해 로드가 중단되었습니다",
+ "noStackTrace": "스택 추적이 사용할 수 없습니다"
+ },
"g": {
"about": "정보",
"add": "추가",
diff --git a/src/locales/ru/main.json b/src/locales/ru/main.json
index c80e915c8f..802cdd2807 100644
--- a/src/locales/ru/main.json
+++ b/src/locales/ru/main.json
@@ -115,6 +115,12 @@
"paused": "Приостановлено",
"resume": "Возобновить загрузку"
},
+ "errorDialog": {
+ "defaultTitle": "Произошла ошибка",
+ "extensionFileHint": "Это может быть связано со следующим скриптом",
+ "loadWorkflowTitle": "Загрузка прервана из-за ошибки при перезагрузке данных рабочего процесса",
+ "noStackTrace": "Стек вызовов недоступен"
+ },
"g": {
"about": "О программе",
"add": "Добавить",
diff --git a/src/locales/zh/main.json b/src/locales/zh/main.json
index b6c5bbeec1..df3667fd6e 100644
--- a/src/locales/zh/main.json
+++ b/src/locales/zh/main.json
@@ -115,6 +115,12 @@
"paused": "已暂停",
"resume": "恢复下载"
},
+ "errorDialog": {
+ "defaultTitle": "发生错误",
+ "extensionFileHint": "这可能是由于以下脚本",
+ "loadWorkflowTitle": "由于重新加载工作流数据出错,加载被中止",
+ "noStackTrace": "无可用堆栈跟踪"
+ },
"g": {
"about": "关于",
"add": "添加",
diff --git a/src/scripts/app.ts b/src/scripts/app.ts
index d185ce5b35..aceef39fc0 100644
--- a/src/scripts/app.ts
+++ b/src/scripts/app.ts
@@ -11,7 +11,7 @@ import _ from 'lodash'
import type { ToastMessageOptions } from 'primevue/toast'
import { reactive } from 'vue'
-import { st } from '@/i18n'
+import { st, t } from '@/i18n'
import type { ResultItem } from '@/schemas/apiSchema'
import {
type ComfyWorkflowJSON,
@@ -1144,56 +1144,10 @@ export class ComfyApp {
this.canvas.ds.scale = graphData.extra.ds.scale
}
} catch (error) {
- let errorHint = []
- // Try extracting filename to see if it was caused by an extension script
- const filename =
- // @ts-expect-error fixme ts strict error
- error.fileName ||
- // @ts-expect-error fixme ts strict error
- (error.stack || '').match(/(\/extensions\/.*\.js)/)?.[1]
- const pos = (filename || '').indexOf('/extensions/')
- if (pos > -1) {
- errorHint.push(
- $el('span', {
- textContent: 'This may be due to the following script:'
- }),
- $el('br'),
- $el('span', {
- style: {
- fontWeight: 'bold'
- },
- textContent: filename.substring(pos)
- })
- )
- }
-
- // Show dialog to let the user know something went wrong loading the data
- this.ui.dialog.show(
- $el('div', [
- $el('p', {
- textContent: 'Loading aborted due to error reloading workflow data'
- }),
- $el('pre', {
- style: { padding: '5px', backgroundColor: 'rgba(255,0,0,0.2)' },
- // @ts-expect-error fixme ts strict error
- textContent: error.toString()
- }),
- $el('pre', {
- style: {
- padding: '5px',
- color: '#ccc',
- fontSize: '10px',
- maxHeight: '50vh',
- overflow: 'auto',
- backgroundColor: 'rgba(0,0,0,0.2)'
- },
- // @ts-expect-error fixme ts strict error
- textContent: error.stack || 'No stacktrace available'
- }),
- ...errorHint
- ]).outerHTML
- )
-
+ useDialogService().showErrorDialog(error, {
+ title: t('errorDialog.loadWorkflowTitle'),
+ errorType: 'loadWorkflowError'
+ })
return
}
for (const node of this.graph.nodes) {
@@ -1384,8 +1338,7 @@ export class ComfyApp {
return !this.lastNodeErrors
}
- // @ts-expect-error fixme ts strict error
- showErrorOnFileLoad(file) {
+ showErrorOnFileLoad(file: File) {
this.ui.dialog.show(
$el('div', [
$el('p', { textContent: `Unable to find workflow in ${file.name}` })
diff --git a/src/services/dialogService.ts b/src/services/dialogService.ts
index b3aa83f6af..d9a5d37324 100644
--- a/src/services/dialogService.ts
+++ b/src/services/dialogService.ts
@@ -1,4 +1,5 @@
import ConfirmationDialogContent from '@/components/dialog/content/ConfirmationDialogContent.vue'
+import ErrorDialogContent from '@/components/dialog/content/ErrorDialogContent.vue'
import ExecutionErrorDialogContent from '@/components/dialog/content/ExecutionErrorDialogContent.vue'
import IssueReportDialogContent from '@/components/dialog/content/IssueReportDialogContent.vue'
import LoadWorkflowWarning from '@/components/dialog/content/LoadWorkflowWarning.vue'
@@ -147,6 +148,52 @@ export const useDialogService = () => {
})
}
+ function parseError(error: Error) {
+ const filename =
+ 'fileName' in error
+ ? (error.fileName as string)
+ : error.stack?.match(/(\/extensions\/.*\.js)/)?.[1]
+
+ const extensionFile = filename
+ ? filename.substring(filename.indexOf('/extensions/'))
+ : undefined
+
+ return {
+ errorMessage: error.toString(),
+ stackTrace: error.stack,
+ extensionFile
+ }
+ }
+
+ /**
+ * Show a error dialog to the user when an error occurs.
+ * @param error The error to show
+ * @param options The options for the dialog
+ */
+ function showErrorDialog(
+ error: unknown,
+ options: {
+ title?: string
+ errorType?: string
+ } = {}
+ ) {
+ const props =
+ error instanceof Error
+ ? parseError(error)
+ : {
+ errorMessage: String(error)
+ }
+
+ dialogStore.showDialog({
+ key: 'global-error',
+ component: ErrorDialogContent,
+ props: {
+ ...props,
+ ...options
+ }
+ })
+ }
+
async function prompt({
title,
message,
@@ -230,6 +277,7 @@ export const useDialogService = () => {
showIssueReportDialog,
showManagerDialog,
showManagerProgressDialog,
+ showErrorDialog,
prompt,
confirm
}