diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/socketInvocationError.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/socketInvocationError.ts
index b09b57bd0c..df1759f3a9 100644
--- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/socketInvocationError.ts
+++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/socketInvocationError.ts
@@ -3,44 +3,16 @@ import type { AppStartListening } from 'app/store/middleware/listenerMiddleware'
import { deepClone } from 'common/util/deepClone';
import { $nodeExecutionStates, upsertExecutionState } from 'features/nodes/hooks/useExecutionState';
import { zNodeStatus } from 'features/nodes/types/invocation';
-import { toast } from 'features/toast/toast';
-import ToastWithSessionRefDescription from 'features/toast/ToastWithSessionRefDescription';
-import { t } from 'i18next';
-import { startCase } from 'lodash-es';
import { socketInvocationError } from 'services/events/actions';
const log = logger('socketio');
-const getTitle = (errorType: string) => {
- if (errorType === 'OutOfMemoryError') {
- return t('toast.outOfMemoryError');
- }
- return t('toast.serverError');
-};
-
-const getDescription = (errorType: string, sessionId: string, isLocal?: boolean) => {
- if (!isLocal) {
- if (errorType === 'OutOfMemoryError') {
- return ToastWithSessionRefDescription({
- message: t('toast.outOfMemoryDescription'),
- sessionId,
- });
- }
- return ToastWithSessionRefDescription({
- message: errorType,
- sessionId,
- });
- }
- return errorType;
-};
-
export const addInvocationErrorEventListener = (startAppListening: AppStartListening) => {
startAppListening({
actionCreator: socketInvocationError,
- effect: (action, { getState }) => {
+ effect: (action) => {
log.error(action.payload, `Invocation error (${action.payload.data.node.type})`);
- const { source_node_id, error_type, error_message, error_traceback, graph_execution_state_id } =
- action.payload.data;
+ const { source_node_id, error_type, error_message, error_traceback } = action.payload.data;
const nes = deepClone($nodeExecutionStates.get()[source_node_id]);
if (nes) {
nes.status = zNodeStatus.enum.FAILED;
@@ -53,19 +25,6 @@ export const addInvocationErrorEventListener = (startAppListening: AppStartListe
};
upsertExecutionState(nes.nodeId, nes);
}
-
- const errorType = startCase(error_type);
- const sessionId = graph_execution_state_id;
- const { isLocal } = getState().config;
-
- toast({
- id: `INVOCATION_ERROR_${errorType}`,
- title: getTitle(errorType),
- status: 'error',
- duration: null,
- description: getDescription(errorType, sessionId, isLocal),
- updateDescription: isLocal ? true : false,
- });
},
});
};
diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/socketQueueItemStatusChanged.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/socketQueueItemStatusChanged.tsx
similarity index 73%
rename from invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/socketQueueItemStatusChanged.ts
rename to invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/socketQueueItemStatusChanged.tsx
index 3b274b2889..b72401d915 100644
--- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/socketQueueItemStatusChanged.ts
+++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/socketQueueItemStatusChanged.tsx
@@ -3,6 +3,8 @@ import type { AppStartListening } from 'app/store/middleware/listenerMiddleware'
import { deepClone } from 'common/util/deepClone';
import { $nodeExecutionStates } from 'features/nodes/hooks/useExecutionState';
import { zNodeStatus } from 'features/nodes/types/invocation';
+import ErrorToastDescription, { getTitleFromErrorType } from 'features/toast/ErrorToastDescription';
+import { toast } from 'features/toast/toast';
import { forEach } from 'lodash-es';
import { queueApi, queueItemsAdapter } from 'services/api/endpoints/queue';
import { socketQueueItemStatusChanged } from 'services/events/actions';
@@ -12,7 +14,7 @@ const log = logger('socketio');
export const addSocketQueueItemStatusChangedEventListener = (startAppListening: AppStartListening) => {
startAppListening({
actionCreator: socketQueueItemStatusChanged,
- effect: async (action, { dispatch }) => {
+ effect: async (action, { dispatch, getState }) => {
// we've got new status for the queue item, batch and queue
const { queue_item, batch_status, queue_status } = action.payload.data;
@@ -54,7 +56,7 @@ export const addSocketQueueItemStatusChangedEventListener = (startAppListening:
])
);
- if (['in_progress'].includes(action.payload.data.queue_item.status)) {
+ if (queue_item.status === 'in_progress') {
forEach($nodeExecutionStates.get(), (nes) => {
if (!nes) {
return;
@@ -67,6 +69,26 @@ export const addSocketQueueItemStatusChangedEventListener = (startAppListening:
clone.outputs = [];
$nodeExecutionStates.setKey(clone.nodeId, clone);
});
+ } else if (queue_item.status === 'failed' && queue_item.error_type) {
+ const { error_type, error_message, session_id } = queue_item;
+ const isLocal = getState().config.isLocal ?? true;
+ const sessionId = session_id;
+
+ toast({
+ id: `INVOCATION_ERROR_${error_type}`,
+ title: getTitleFromErrorType(error_type),
+ status: 'error',
+ duration: null,
+ description: (
+
+ ),
+ updateDescription: isLocal ? true : false,
+ });
}
},
});
diff --git a/invokeai/frontend/web/src/features/toast/ErrorToastDescription.tsx b/invokeai/frontend/web/src/features/toast/ErrorToastDescription.tsx
new file mode 100644
index 0000000000..b9729c1510
--- /dev/null
+++ b/invokeai/frontend/web/src/features/toast/ErrorToastDescription.tsx
@@ -0,0 +1,60 @@
+import { Flex, IconButton, Text } from '@invoke-ai/ui-library';
+import { t } from 'i18next';
+import { upperFirst } from 'lodash-es';
+import { useMemo } from 'react';
+import { useTranslation } from 'react-i18next';
+import { PiCopyBold } from 'react-icons/pi';
+
+function onCopy(sessionId: string) {
+ navigator.clipboard.writeText(sessionId);
+}
+
+const ERROR_TYPE_TO_TITLE: Record = {
+ OutOfMemoryError: 'toast.outOfMemoryError',
+};
+
+const COMMERCIAL_ERROR_TYPE_TO_DESC: Record = {
+ OutOfMemoryError: 'toast.outOfMemoryErrorDesc',
+};
+
+export const getTitleFromErrorType = (errorType: string) => {
+ return t(ERROR_TYPE_TO_TITLE[errorType] ?? 'toast.serverError');
+};
+
+type Props = { errorType: string; errorMessage?: string | null; sessionId: string; isLocal: boolean };
+
+export default function ErrorToastDescription({ errorType, errorMessage, sessionId, isLocal }: Props) {
+ const { t } = useTranslation();
+ const description = useMemo(() => {
+ // Special handling for commercial error types
+ const descriptionTKey = isLocal ? null : COMMERCIAL_ERROR_TYPE_TO_DESC[errorType];
+ if (descriptionTKey) {
+ return t(descriptionTKey);
+ }
+ if (errorMessage) {
+ return upperFirst(errorMessage);
+ }
+ }, [errorMessage, errorType, isLocal, t]);
+ return (
+
+ {description && {description}}
+ {!isLocal && (
+
+
+ {t('toast.sessionRef', { sessionId })}
+
+ }
+ onClick={onCopy.bind(null, sessionId)}
+ variant="ghost"
+ sx={sx}
+ />
+
+ )}
+
+ );
+}
+
+const sx = { svg: { fill: 'base.50' } };
diff --git a/invokeai/frontend/web/src/features/toast/ToastWithSessionRefDescription.tsx b/invokeai/frontend/web/src/features/toast/ToastWithSessionRefDescription.tsx
deleted file mode 100644
index 9d2999e765..0000000000
--- a/invokeai/frontend/web/src/features/toast/ToastWithSessionRefDescription.tsx
+++ /dev/null
@@ -1,30 +0,0 @@
-import { Flex, IconButton, Text } from '@invoke-ai/ui-library';
-import { t } from 'i18next';
-import { PiCopyBold } from 'react-icons/pi';
-
-function onCopy(sessionId: string) {
- navigator.clipboard.writeText(sessionId);
-}
-
-type Props = { message: string; sessionId: string };
-
-export default function ToastWithSessionRefDescription({ message, sessionId }: Props) {
- return (
-
- {message}
-
- {t('toast.sessionRef', { sessionId })}
- }
- onClick={onCopy.bind(null, sessionId)}
- variant="ghost"
- sx={sx}
- />
-
-
- );
-}
-
-const sx = { svg: { fill: 'base.50' } };