feat: improve Google Calendar and Gmail connectors with enhanced error handling

- Added user-friendly re-authentication messages for expired or revoked tokens in both Google Calendar and Gmail connectors.
- Updated error handling in indexing tasks to log specific authentication errors and provide clearer feedback to users.
- Enhanced the connector UI to handle indexing failures more effectively, improving overall user experience.
This commit is contained in:
Anish Sarkar 2026-01-23 18:57:10 +05:30
parent 29382070aa
commit 8d8f69545e
8 changed files with 113 additions and 12 deletions

View file

@ -142,6 +142,12 @@ class GoogleCalendarConnector:
flag_modified(connector, "config")
await self._session.commit()
except Exception as e:
error_str = str(e)
# Check if this is an invalid_grant error (token expired/revoked)
if "invalid_grant" in error_str.lower() or "token has been expired or revoked" in error_str.lower():
raise Exception(
"Google Calendar authentication failed. Please re-authenticate."
) from e
raise Exception(
f"Failed to refresh Google OAuth credentials: {e!s}"
) from e
@ -165,6 +171,10 @@ class GoogleCalendarConnector:
self.service = build("calendar", "v3", credentials=credentials)
return self.service
except Exception as e:
error_str = str(e)
# If the error already contains a user-friendly re-authentication message, preserve it
if "re-authenticate" in error_str.lower() or "expired or been revoked" in error_str.lower() or "authentication failed" in error_str.lower():
raise Exception(error_str) from e
raise Exception(f"Failed to create Google Calendar service: {e!s}") from e
async def get_calendars(self) -> tuple[list[dict[str, Any]], str | None]:
@ -271,6 +281,10 @@ class GoogleCalendarConnector:
return events, None
except Exception as e:
error_str = str(e)
# If the error already contains a user-friendly re-authentication message, preserve it
if "re-authenticate" in error_str.lower() or "expired or been revoked" in error_str.lower() or "authentication failed" in error_str.lower():
return [], error_str
return [], f"Error fetching events: {e!s}"
def format_event_to_markdown(self, event: dict[str, Any]) -> str:

View file

@ -141,6 +141,12 @@ class GoogleGmailConnector:
flag_modified(connector, "config")
await self._session.commit()
except Exception as e:
error_str = str(e)
# Check if this is an invalid_grant error (token expired/revoked)
if "invalid_grant" in error_str.lower() or "token has been expired or revoked" in error_str.lower():
raise Exception(
"Gmail authentication failed. Please re-authenticate."
) from e
raise Exception(
f"Failed to refresh Google OAuth credentials: {e!s}"
) from e
@ -164,6 +170,10 @@ class GoogleGmailConnector:
self.service = build("gmail", "v1", credentials=credentials)
return self.service
except Exception as e:
error_str = str(e)
# If the error already contains a user-friendly re-authentication message, preserve it
if "re-authenticate" in error_str.lower() or "expired or been revoked" in error_str.lower() or "authentication failed" in error_str.lower():
raise Exception(error_str) from e
raise Exception(f"Failed to create Gmail service: {e!s}") from e
async def get_user_profile(self) -> tuple[dict[str, Any], str | None]:
@ -225,6 +235,10 @@ class GoogleGmailConnector:
return messages, None
except Exception as e:
error_str = str(e)
# If the error already contains a user-friendly re-authentication message, preserve it
if "re-authenticate" in error_str.lower() or "expired or been revoked" in error_str.lower() or "authentication failed" in error_str.lower():
return [], error_str
return [], f"Error fetching messages list: {e!s}"
async def get_message_details(

View file

@ -246,13 +246,20 @@ async def index_google_calendar_events(
)
return 0, None
else:
# Check if this is an authentication error that requires re-authentication
error_message = error
error_type = "APIError"
if "re-authenticate" in error.lower() or "expired or been revoked" in error.lower() or "authentication failed" in error.lower():
error_message = "Google Calendar authentication failed. Please re-authenticate."
error_type = "AuthenticationError"
await task_logger.log_task_failure(
log_entry,
f"Failed to get Google Calendar events: {error}",
"API Error",
{"error_type": "APIError"},
error_message,
error,
{"error_type": error_type},
)
return 0, f"Failed to get Google Calendar events: {error}"
return 0, error_message
logger.info(f"Retrieved {len(events)} events from Google Calendar API")

View file

@ -170,10 +170,20 @@ async def index_google_gmail_messages(
)
if error:
# Check if this is an authentication error that requires re-authentication
error_message = error
error_type = "APIError"
if "re-authenticate" in error.lower() or "expired or been revoked" in error.lower() or "authentication failed" in error.lower():
error_message = "Gmail authentication failed. Please re-authenticate."
error_type = "AuthenticationError"
await task_logger.log_task_failure(
log_entry, f"Failed to fetch messages: {error}", {}
log_entry,
error_message,
error,
{"error_type": error_type}
)
return 0, f"Failed to fetch Gmail messages: {error}"
return 0, error_message
if not messages:
success_msg = "No Google gmail messages found in the specified date range"