diff --git a/packages/twenty-front/src/modules/ui/feedback/snack-bar-manager/components/SnackBar.tsx b/packages/twenty-front/src/modules/ui/feedback/snack-bar-manager/components/SnackBar.tsx
index 833a943031..048ed520e2 100644
--- a/packages/twenty-front/src/modules/ui/feedback/snack-bar-manager/components/SnackBar.tsx
+++ b/packages/twenty-front/src/modules/ui/feedback/snack-bar-manager/components/SnackBar.tsx
@@ -1,3 +1,4 @@
+import { sanitizeMessageToRenderInSnackbar } from '@/ui/feedback/snack-bar-manager/utils/sanitizeMessageToRenderInSnackbar';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { useLingui } from '@lingui/react/macro';
@@ -196,13 +197,19 @@ export const SnackBar = ({
}
};
+ const sanitizedMessage = sanitizeMessageToRenderInSnackbar(message);
+ const sanitizedDetailedMessage =
+ sanitizeMessageToRenderInSnackbar(detailedMessage);
+
return (
{icon}
- {message}
+ {sanitizedMessage ?? ''}
{!!onCancel && }
@@ -220,8 +227,8 @@ export const SnackBar = ({
)}
- {detailedMessage && (
- {detailedMessage}
+ {isDefined(sanitizedDetailedMessage) && (
+ {sanitizedDetailedMessage}
)}
{link && {link.text}}
diff --git a/packages/twenty-front/src/modules/ui/feedback/snack-bar-manager/hooks/useSnackBar.ts b/packages/twenty-front/src/modules/ui/feedback/snack-bar-manager/hooks/useSnackBar.ts
index e060a36446..558f875d0e 100644
--- a/packages/twenty-front/src/modules/ui/feedback/snack-bar-manager/hooks/useSnackBar.ts
+++ b/packages/twenty-front/src/modules/ui/feedback/snack-bar-manager/hooks/useSnackBar.ts
@@ -142,6 +142,7 @@ export const useSnackBar = () => {
: apolloError
? getErrorMessageFromApolloError(apolloError)
: t`An error occurred.`;
+
setSnackBarQueue({
id: uuidv4(),
message: errorMessage,
diff --git a/packages/twenty-front/src/modules/ui/feedback/snack-bar-manager/utils/sanitizeMessageToRenderInSnackbar.ts b/packages/twenty-front/src/modules/ui/feedback/snack-bar-manager/utils/sanitizeMessageToRenderInSnackbar.ts
new file mode 100644
index 0000000000..c361c8b50d
--- /dev/null
+++ b/packages/twenty-front/src/modules/ui/feedback/snack-bar-manager/utils/sanitizeMessageToRenderInSnackbar.ts
@@ -0,0 +1,24 @@
+import { t } from '@lingui/core/macro';
+import { isDefined } from 'twenty-shared/utils';
+
+export const sanitizeMessageToRenderInSnackbar = (
+ messageToRenderInSnackbar: any,
+) => {
+ if (!isDefined(messageToRenderInSnackbar)) {
+ return null;
+ } else if (
+ typeof messageToRenderInSnackbar === 'string' ||
+ typeof messageToRenderInSnackbar === 'string' ||
+ typeof messageToRenderInSnackbar === 'boolean'
+ ) {
+ return `${messageToRenderInSnackbar}`;
+ } else if (typeof messageToRenderInSnackbar === 'object') {
+ try {
+ return JSON.stringify(messageToRenderInSnackbar);
+ } catch {
+ return t`Cannot display message`;
+ }
+ } else {
+ return t`Cannot display message`;
+ }
+};
diff --git a/packages/twenty-front/src/utils/get-error-message-from-apollo-error.util.ts b/packages/twenty-front/src/utils/get-error-message-from-apollo-error.util.ts
index e851da9214..fae7fc6662 100644
--- a/packages/twenty-front/src/utils/get-error-message-from-apollo-error.util.ts
+++ b/packages/twenty-front/src/utils/get-error-message-from-apollo-error.util.ts
@@ -1,14 +1,20 @@
import { type ApolloError } from '@apollo/client';
+import { type MessageDescriptor } from '@lingui/core';
import { t } from '@lingui/core/macro';
+import { type Nullable } from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
export const getErrorMessageFromApolloError = (error: ApolloError): string => {
- if (!isDefined(error.graphQLErrors?.[0]?.extensions?.userFriendlyMessage)) {
+ const userFriendlyMessage = error.graphQLErrors?.[0]?.extensions
+ ?.userFriendlyMessage as Nullable;
+
+ if (!isDefined(userFriendlyMessage)) {
return t`An error occurred.`;
}
- return (
- (error.graphQLErrors[0].extensions?.userFriendlyMessage as string) ??
- t`An error occurred.`
- );
+ if (typeof userFriendlyMessage === 'object' && 'id' in userFriendlyMessage) {
+ return t(userFriendlyMessage);
+ }
+
+ return userFriendlyMessage;
};
diff --git a/packages/twenty-server/src/engine/core-modules/graphql/hooks/use-graphql-error-handler.hook.ts b/packages/twenty-server/src/engine/core-modules/graphql/hooks/use-graphql-error-handler.hook.ts
index 70b17b076d..30ed5ee939 100644
--- a/packages/twenty-server/src/engine/core-modules/graphql/hooks/use-graphql-error-handler.hook.ts
+++ b/packages/twenty-server/src/engine/core-modules/graphql/hooks/use-graphql-error-handler.hook.ts
@@ -248,6 +248,9 @@ export const useGraphQLErrorHandlerHook = <
onValidate: ({ context, validateFn, params: { documentAST, schema } }) => {
const errors = validateFn(schema, documentAST);
+ const userLocale = context.req.locale ?? SOURCE_LOCALE;
+ const i18n = options.i18nService.getI18nInstance(userLocale);
+
if (Array.isArray(errors) && errors.length > 0) {
const headers = context.req.headers;
const currentMetadataVersion = context.req.workspaceMetadataVersion;
@@ -267,9 +270,12 @@ export const useGraphQLErrorHandlerHook = <
options.metricsService.incrementCounter({
key: MetricsKeys.SchemaVersionMismatch,
});
+
throw new GraphQLError(SCHEMA_MISMATCH_ERROR, {
extensions: {
- userFriendlyMessage: msg`Your workspace has been updated with a new data model. Please refresh the page.`,
+ userFriendlyMessage: i18n._(
+ msg`Your workspace has been updated with a new data model. Please refresh the page.`,
+ ),
},
});
}
@@ -297,7 +303,9 @@ export const useGraphQLErrorHandlerHook = <
throw new GraphQLError(APP_VERSION_MISMATCH_ERROR, {
extensions: {
code: APP_VERSION_MISMATCH_CODE,
- userFriendlyMessage: msg`Your app version is out of date. Please refresh the page to continue.`,
+ userFriendlyMessage: i18n._(
+ msg`Your app version is out of date. Please refresh the page to continue.`,
+ ),
},
});
}