Improve email sending error handling (#732)

<!-- ELLIPSIS_HIDDEN -->



> [!IMPORTANT]
> Improves email sending error handling in `emails.tsx` by introducing
`StatusError` and a centralized error handling function for consistent
and user-friendly error reporting.
> 
>   - **Error Handling**:
> - Introduced `StatusError` for better error reporting in `emails.tsx`.
> - Added `handleError` function in `sendEmail()` to log errors and
throw `StatusError` with a user-friendly message.
>   - **Email Sending Logic**:
> - Updated `sendEmail()` to use `handleError` for consistent error
handling.
> - Ensures retries for transient errors and logs specific errors for
shared email configurations.
>   - **Misc**:
> - Minor refactoring in `sendEmail()` to improve code clarity and
maintainability.
> 
> <sup>This description was created by </sup>[<img alt="Ellipsis"
src="https://img.shields.io/badge/Ellipsis-blue?color=175173">](https://www.ellipsis.dev?ref=stack-auth%2Fstack-auth&utm_source=github&utm_medium=referral)<sup>
for b6dad5dac1. You can
[customize](https://app.ellipsis.dev/stack-auth/settings/summaries) this
summary. It will automatically update as commits are pushed.</sup>

<!-- ELLIPSIS_HIDDEN -->

Co-authored-by: Konsti Wohlwend <n2d4xc@gmail.com>
This commit is contained in:
Zai Shi 2025-07-16 22:57:55 +02:00 committed by GitHub
parent 507d6dbc39
commit c7d2601880
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -5,7 +5,7 @@ import { TEditorConfiguration } from '@stackframe/stack-emails/dist/editor/docum
import { EMAIL_TEMPLATES_METADATA, renderEmailTemplate } from '@stackframe/stack-emails/dist/utils';
import { UsersCrud } from '@stackframe/stack-shared/dist/interface/crud/users';
import { getEnvVariable } from '@stackframe/stack-shared/dist/utils/env';
import { StackAssertionError, captureError } from '@stackframe/stack-shared/dist/utils/errors';
import { StackAssertionError, StatusError, captureError } from '@stackframe/stack-shared/dist/utils/errors';
import { filterUndefined, omit, pick } from '@stackframe/stack-shared/dist/utils/objects';
import { runAsynchronously, wait } from '@stackframe/stack-shared/dist/utils/promises';
import { Result } from '@stackframe/stack-shared/dist/utils/results';
@ -285,7 +285,17 @@ export async function sendEmail(options: SendEmailOptions) {
throw new StackAssertionError("No recipient email address provided to sendEmail", omit(options, ['emailConfig']));
}
return Result.orThrow(await Result.retry(async (attempt) => {
const errorMessage = "Failed to send email. If you are the admin of this project, please check the email configuration and try again.";
const handleError = (error: any) => {
console.warn("Failed to send email", error);
if (options.emailConfig.type === 'shared') {
captureError("failed-to-send-email-to-shared-email-config", error);
}
throw new StatusError(400, errorMessage);
};
const result = await Result.retry(async (attempt) => {
const result = await sendEmailWithoutRetries(options);
if (result.status === 'error') {
@ -302,12 +312,15 @@ export async function sendEmail(options: SendEmailOptions) {
return Result.error(result.error);
}
// TODO if using custom email config, we should notify the developer instead of throwing an error
throw new StackAssertionError('Failed to send email: ' + result.error.rawError?.message, extraData);
handleError(extraData);
}
return result;
}, 3, { exponentialDelayBase: 2000 }));
}, 3, { exponentialDelayBase: 2000 });
if (result.status === 'error') {
handleError(result.error);
}
}
export async function sendEmailFromTemplate(options: {