Skip to content

Commit 0849dda

Browse files
authored
Gmail error handling fixes (#16719)
- Replaced direct instance checks for GaxiosError with a utility function isGmailApiError for better error handling consistency across services. - Debug logs
1 parent d30580f commit 0849dda

5 files changed

Lines changed: 100 additions & 12 deletions

File tree

packages/twenty-server/src/modules/connected-account/email-alias-manager/drivers/google/services/google-email-alias-error-handler.service.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
import { Injectable, Logger } from '@nestjs/common';
22

3-
import { GaxiosError } from 'gaxios';
4-
53
import {
64
MessageImportDriverException,
75
MessageImportDriverExceptionCode,
86
} from 'src/modules/messaging/message-import-manager/drivers/exceptions/message-import-driver.exception';
7+
import { isGmailApiError } from 'src/modules/messaging/message-import-manager/drivers/gmail/utils/is-gmail-api-error.util';
98
import { isGmailNetworkError } from 'src/modules/messaging/message-import-manager/drivers/gmail/utils/is-gmail-network-error.util';
109
import { parseGmailApiError } from 'src/modules/messaging/message-import-manager/drivers/gmail/utils/parse-gmail-api-error.util';
1110
import { parseGmailNetworkError } from 'src/modules/messaging/message-import-manager/drivers/gmail/utils/parse-gmail-network-error.util';
@@ -17,12 +16,16 @@ export class GmailEmailAliasErrorHandlerService {
1716
constructor() {}
1817

1918
public handleError(error: unknown): void {
20-
this.logger.error(`Google: Error getting email aliases: ${error}`);
19+
const constructorName = (error as unknown)?.constructor?.name ?? 'Unknown';
20+
21+
this.logger.error(
22+
`Google: Error getting email aliases: ${error}, constructor: ${constructorName}`,
23+
);
2124
if (isGmailNetworkError(error)) {
2225
throw parseGmailNetworkError(error);
2326
}
2427

25-
if (error instanceof GaxiosError) {
28+
if (isGmailApiError(error)) {
2629
throw parseGmailApiError(error);
2730
}
2831

packages/twenty-server/src/modules/messaging/message-folder-manager/drivers/gmail/services/gmail-folders-error-handler.service.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
import { Injectable, Logger } from '@nestjs/common';
22

3-
import { GaxiosError } from 'gaxios';
4-
53
import {
64
MessageImportDriverException,
75
MessageImportDriverExceptionCode,
86
} from 'src/modules/messaging/message-import-manager/drivers/exceptions/message-import-driver.exception';
7+
import { isGmailApiError } from 'src/modules/messaging/message-import-manager/drivers/gmail/utils/is-gmail-api-error.util';
98
import { isGmailNetworkError } from 'src/modules/messaging/message-import-manager/drivers/gmail/utils/is-gmail-network-error.util';
109
import { parseGmailApiError } from 'src/modules/messaging/message-import-manager/drivers/gmail/utils/parse-gmail-api-error.util';
1110
import { parseGmailNetworkError } from 'src/modules/messaging/message-import-manager/drivers/gmail/utils/parse-gmail-network-error.util';
@@ -17,14 +16,16 @@ export class GmailFoldersErrorHandlerService {
1716
constructor() {}
1817

1918
public handleError(error: unknown): void {
19+
const constructorName = (error as unknown)?.constructor?.name ?? 'Unknown';
20+
2021
this.logger.error(
21-
`Gmail: Error fetching folders: ${JSON.stringify(error)}`,
22+
`Gmail: Error fetching folders: ${JSON.stringify(error)}, constructor: ${constructorName}`,
2223
);
2324
if (isGmailNetworkError(error)) {
2425
throw parseGmailNetworkError(error);
2526
}
2627

27-
if (error instanceof GaxiosError) {
28+
if (isGmailApiError(error)) {
2829
throw parseGmailApiError(error);
2930
}
3031

packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/services/gmail-message-list-fetch-error-handler.service.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
import { Injectable, Logger } from '@nestjs/common';
22

3-
import { GaxiosError } from 'gaxios';
4-
53
import {
64
MessageImportDriverException,
75
MessageImportDriverExceptionCode,
86
} from 'src/modules/messaging/message-import-manager/drivers/exceptions/message-import-driver.exception';
7+
import { isGmailApiError } from 'src/modules/messaging/message-import-manager/drivers/gmail/utils/is-gmail-api-error.util';
98
import { isGmailNetworkError } from 'src/modules/messaging/message-import-manager/drivers/gmail/utils/is-gmail-network-error.util';
109
import { parseGmailApiError } from 'src/modules/messaging/message-import-manager/drivers/gmail/utils/parse-gmail-api-error.util';
1110
import { parseGmailNetworkError } from 'src/modules/messaging/message-import-manager/drivers/gmail/utils/parse-gmail-network-error.util';
@@ -17,14 +16,16 @@ export class GmailMessageListFetchErrorHandler {
1716
constructor() {}
1817

1918
public handleError(error: unknown): void {
19+
const constructorName = (error as unknown)?.constructor?.name ?? 'Unknown';
20+
2021
this.logger.error(
21-
`Gmail: Error fetching message list: ${JSON.stringify(error)}`,
22+
`Gmail: Error fetching message list: ${JSON.stringify(error)}, constructor: ${constructorName}`,
2223
);
2324
if (isGmailNetworkError(error)) {
2425
throw parseGmailNetworkError(error);
2526
}
2627

27-
if (error instanceof GaxiosError) {
28+
if (isGmailApiError(error)) {
2829
throw parseGmailApiError(error);
2930
}
3031

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { GaxiosError } from 'gaxios';
2+
3+
import { isGmailApiError } from 'src/modules/messaging/message-import-manager/drivers/gmail/utils/is-gmail-api-error.util';
4+
5+
describe('isGmailApiError', () => {
6+
it('should detect Gmail API error shape even when instanceof GaxiosError fails', () => {
7+
const originalError = new GaxiosError(
8+
'Rate limit exceeded',
9+
{},
10+
{
11+
status: 429,
12+
statusText: 'Too Many Requests',
13+
headers: {},
14+
config: {},
15+
request: { responseURL: '' },
16+
data: {
17+
error: {
18+
errors: [
19+
{ reason: 'rateLimitExceeded', message: 'Rate limit exceeded' },
20+
],
21+
},
22+
},
23+
},
24+
);
25+
26+
const serialized = JSON.parse(JSON.stringify(originalError));
27+
28+
expect(serialized instanceof GaxiosError).toBe(false);
29+
expect(isGmailApiError(serialized)).toBe(true);
30+
});
31+
32+
it('should detect error when data.error is a string instead of object', () => {
33+
const error = {
34+
response: {
35+
status: 429,
36+
data: {
37+
error: 'userRateLimitExceeded',
38+
error_description: 'User Rate Limit Exceeded',
39+
},
40+
},
41+
};
42+
43+
expect(isGmailApiError(error)).toBe(true);
44+
});
45+
46+
it('should reject network errors that have no response', () => {
47+
const networkError = new Error('connect ECONNREFUSED');
48+
49+
expect(isGmailApiError(networkError)).toBe(false);
50+
});
51+
});
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { type GaxiosError } from 'gaxios';
2+
import { z } from 'zod';
3+
4+
const gmailApiErrorSchema = z.object({
5+
response: z.object({
6+
status: z.number().optional(),
7+
data: z
8+
.object({
9+
error: z
10+
.union([
11+
z.object({
12+
errors: z
13+
.array(
14+
z.object({
15+
reason: z.string().optional(),
16+
message: z.string().optional(),
17+
}),
18+
)
19+
.optional(),
20+
}),
21+
z.string(),
22+
])
23+
.optional(),
24+
error_description: z.string().optional(),
25+
})
26+
.optional(),
27+
}),
28+
});
29+
30+
export const isGmailApiError = (error: unknown): error is GaxiosError => {
31+
return gmailApiErrorSchema.safeParse(error).success;
32+
};

0 commit comments

Comments
 (0)