monicahq/monica · error · Exception

Only email can be sent.

Error message

Only email can be sent.

What it means

SendTestEmail sends a test message on a user notification channel. It loads the channel belonging to the author via findOrFail (unknown id -> 404) and then requires type === UserNotificationChannel::TYPE_EMAIL ('email'); any other type throws a bare \Exception which Laravel renders as a 500.

Source

Thrown at app/Domains/Settings/ManageNotificationChannels/Services/SendTestEmail.php:63

    public function execute(array $data): UserNotificationChannel
    {
        $this->data = $data;
        $this->validate();
        $this->send();
        $this->log();

        return $this->userNotificationChannel;
    }

    private function validate(): void
    {
        $this->validateRules($this->data);

        $this->userNotificationChannel = $this->author->notificationChannels()
            ->findOrFail($this->data['user_notification_channel_id']);

        if ($this->userNotificationChannel->type !== UserNotificationChannel::TYPE_EMAIL) {
            throw new Exception('Only email can be sent.');
        }
    }

    private function send(): void
    {
        Mail::to($this->userNotificationChannel->content)->send(
            new TestEmailSent($this->userNotificationChannel)
        );
    }

    private function log(): void
    {
        UserNotificationSent::create([
            'user_notification_channel_id' => $this->userNotificationChannel->id,
            'sent_at' => Carbon::now(),
            'subject_line' => trans('Test email for Monica'),
        ]);
    }

View on GitHub (pinned to e08e917341)

Solutions

  1. Pass the id of a channel whose type is 'email'
  2. Reload the notification channels list before triggering the test send
  3. Only offer 'send test email' for email channels in the UI
  4. Catch the exception in the controller and return 422 with the message instead of a 500

Example fix

// before
app(SendTestEmail::class)->execute([
    'user_notification_channel_id' => $channelId, // might be a telegram channel
]);

// after
$channel = $author->notificationChannels()->findOrFail($channelId);
abort_unless($channel->type === UserNotificationChannel::TYPE_EMAIL, 422, 'Only email can be sent.');
app(SendTestEmail::class)->execute([
    'user_notification_channel_id' => $channel->id,
]);
Defensive patterns

Strategy: validation

Validate before calling

// Validate before calling: the channel must exist and be an email channel
$channel = $author->notificationChannels()->find($data['user_notification_channel_id']);

if ($channel === null) {
    throw ValidationException::withMessages(['user_notification_channel_id' => 'Channel not found.']);
}
if ($channel->type !== UserNotificationChannel::TYPE_EMAIL) {
    throw ValidationException::withMessages(['user_notification_channel_id' => 'Only email channels can receive a test email.']);
}

Type guard

function isEmailChannel(?UserNotificationChannel $channel): bool
{
    return $channel !== null
        && $channel->type === UserNotificationChannel::TYPE_EMAIL;
}

Try / catch

try {
    app(SendTestEmail::class)->execute($data);
} catch (\Exception $e) {
    if ($e->getMessage() === 'Only email can be sent.') {
        // wrong channel type: surface as 422 and refresh the channel list
        throw ValidationException::withMessages(['user_notification_channel_id' => $e->getMessage()]);
    }
    throw $e;
}

Prevention

When it happens

Trigger: Invoking the 'send test email' action with the user_notification_channel_id of a channel whose type is 'telegram' (the only other type in this codebase) — typically a stale frontend still holding an id from before the channel was created or switched.

Common situations: UI channel list not refreshed after editing channels, mixed-up ids between the email and telegram channel forms, or API consumers hard-coding the wrong channel id.

Related errors


AI-assisted analysis of monicahq/monica@e08e917341 (2026-08-17). Data as JSON: /api/errors/c3142e3f4bb18850. Report an issue: GitHub.