{"record":{"id":"e4fc2a97e9b3eff7","repo":"BookStackApp/BookStack","slug":"auth-email-confirm-send-error","errorCode":null,"errorMessage":"auth.email_confirm_send_error","messagePattern":"auth\\.email_confirm_send_error","errorType":"exception","errorClass":"UserRegistrationException","httpStatus":null,"severity":"error","filePath":"app/Access/RegistrationService.php","lineNumber":118,"sourceCode":"        // Assign a social account if given\n        if ($socialAccount) {\n            $newUser->socialAccounts()->save($socialAccount);\n        }\n\n        Activity::add(ActivityType::AUTH_REGISTER, $socialAccount ?? $newUser);\n        Theme::dispatch(ThemeEvents::AUTH_REGISTER, $authSystem, $newUser);\n\n        // Start the email confirmation flow if required\n        if ($this->emailConfirmationService->confirmationRequired() && !$emailConfirmed) {\n            $newUser->save();\n\n            try {\n                $this->emailConfirmationService->sendConfirmation($newUser);\n                session()->flash('sent-email-confirmation', true);\n            } catch (Exception $e) {\n                $message = trans('auth.email_confirm_send_error');\n\n                throw new UserRegistrationException($message, '/register/confirm');\n            }\n        }\n\n        return $newUser;\n    }\n\n    /**\n     * Ensure that the given email meets any active email domain registration restrictions.\n     * Throws if restrictions are active and the email does not match an allowed domain.\n     *\n     * @throws UserRegistrationException\n     */\n    protected function ensureEmailDomainAllowed(string $userEmail): void\n    {\n        $registrationRestrict = setting('registration-restrict');\n\n        if (!$registrationRestrict) {\n            return;","sourceCodeStart":100,"sourceCodeEnd":136,"githubUrl":"https://github.com/BookStackApp/BookStack/blob/18f8469a1c72f8cc8497e9372635e6dea5028071/app/Access/RegistrationService.php#L100-L136","documentation":"This is a UserRegistrationException thrown by BookStack's RegistrationService (registerUser) when the email-confirmation mail cannot be sent to a newly registered user. After creating the account, if email confirmation is required, the service calls EmailConfirmationService::sendConfirmation; any Exception from that call is caught, translated via the 'auth.email_confirm_send_error' language key, and re-thrown as a UserRegistrationException with redirect '/register/confirm'. The user account itself has already been created; only the confirmation email dispatch failed.","triggerScenarios":"A user completes the registration form while email confirmation is enabled (confirmationRequired() returns true and the account is not pre-confirmed), the user record is created, and sendConfirmation() throws — typically because the configured mailer (SMTP/mailgun/SES/log driver) fails: SMTP connection refused/auth failure, missing mail env vars (MAIL_HOST, MAIL_USERNAME, etc.), or a mail-driver API error.","commonSituations":"Self-hosted BookStack instances where SMTP credentials were changed or the mail server is down; Docker deployments missing MAIL_* environment variables; using a mail service whose API key expired; local dev environments with no working mail transport configured (e.g. 'mail' driver on a host without sendmail); firewall blocking outbound port 25/587.","solutions":["Test the mail configuration: run 'php artisan bookstack:send-test-email' (or check the mail settings in .env: MAIL_HOST, MAIL_PORT, MAIL_USERNAME, MAIL_PASSWORD, MAIL_ENCRYPTION) and fix credentials/host so sendConfirmation succeeds.","Verify the account was still created (users table) — if so, the user can log in and you can resend confirmation from the user-profile page instead of re-registering.","If email confirmation is not needed, disable it ('Require email confirmation' toggle under Settings > Registration) so the sendConfirmation path is skipped.","In dev, switch MAIL_DRIVER to 'log' so confirmation 'emails' are written to the log and the flow completes without a real mail server.","Check mail server logs / firewall for rejected connections (port 25 often blocked on cloud hosts); use port 587 with STARTTLS via a relay."],"exampleFix":"// before (.env, broken SMTP)\nMAIL_HOST=mail.example.com\nMAIL_USERNAME=\nMAIL_PASSWORD=\n\n// after (.env, working SMTP)\nMAIL_HOST=smtp.example.com\nMAIL_PORT=587\nMAIL_USERNAME=no-reply@example.com\nMAIL_PASSWORD=secret\nMAIL_ENCRYPTION=tls","handlingStrategy":"try-catch","validationCode":"// Before triggering registration (or before resend), verify mail config is usable:\n$transportOk = config('mail.mailers.smtp.host') !== null;\n// Better: actually probe the transport\ncatchable_check: try {\n    Mail::raw('probe', function ($m) { $m->to('probe@example.com')->subject('probe'); });\n} catch (\\Throwable $e) {\n    // mail transport misconfigured — fix MAIL_* settings before registering users\n}","typeGuard":null,"tryCatchPattern":"use BookStack\\Exceptions\\UserRegistrationException;\n\ntry {\n    $user = $registrationService->registerUser($userData, $socialAccount, false);\n} catch (UserRegistrationException $e) {\n    // message is the translated 'auth.email_confirm_send_error'; account may already exist\n    logger()->warning('Registration confirmation email failed: ' . $e->getMessage());\n    return redirect($e->getRedirect()); // '/register/confirm'\n}","preventionTips":["Run 'php artisan bookstack:send-test-email' after every mail config change","Keep MAIL_* env vars in a validated checklist for deployments (host, port, username, password, encryption)","Use a reliable relay (port 587/STARTTLS) since outbound port 25 is commonly blocked","Monitor the activity/logs for this exception to detect mail outages before users register"],"tags":["php","laravel","email","registration","smtp"],"backgroundTag":"email-send-failed","analyzedSha":"18f8469a1c72f8cc8497e9372635e6dea5028071","analyzedAt":"2026-09-02T19:49:33.068Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T02:17:09.455Z"}