# Troubleshooting

Solutions for common issues you may encounter while using MailTrixy.

## Email Not Syncing

If your email accounts are not syncing new messages, check the following:

- Queue processing — Verify that your queue worker is running. Check `php artisan queue:work --queue=email-sync` or confirm Middleware Mode is active.
- IMAP credentials — Go to the email account settings and test the connection. Incorrect passwords or expired OAuth tokens will cause sync failures.
- IMAP extension — Ensure the PHP IMAP extension is installed and enabled: `php -m | grep imap`.
- Firewall — Confirm your server allows outbound connections on ports 993 (IMAP SSL) and 587 (SMTP TLS).
- Check logs — Review `storage/logs/laravel.log` for IMAP connection errors.

## OAuth Connection Failed

If Gmail or Outlook OAuth connection fails:

- Verify your OAuth client ID and client secret are correctly entered in the admin panel settings.
- Ensure the redirect URI in your Google/Microsoft developer console matches exactly: `https://your-domain.com/oauth/callback/gmail` or `https://your-domain.com/oauth/callback/outlook`.
- For Gmail, make sure the Gmail API and People API are enabled in the Google Cloud Console.
- For Outlook, verify the application has the `Mail.ReadWrite`, `Mail.Send`, and `offline_access` permissions.
- Check that your `APP_URL` in `.env` matches the domain configured in OAuth redirect URIs.

## AI Replies Not Generating

If AI reply generation is not working:

- Confirm that at least one AI provider is configured with a valid API key in Admin Panel → AI Providers.
- Check that the workspace has not exceeded its AI spending cap. View current usage in Settings → AI Usage.
- Verify the queue worker is processing the `default` queue where AI jobs run.
- Check `storage/logs/laravel.log` for API error responses from the AI provider (rate limits, invalid key, model not available).
- Ensure the AI provider's API endpoint is reachable from your server (not blocked by firewall).

## Campaign Not Sending

If a campaign remains in "Sending" status without progress:

- Verify the `campaigns` queue worker is running.
- Check that the sending email account's SMTP credentials are valid.
- Review failed jobs: `php artisan queue:failed`.
- Ensure the recipient list is not empty and contacts have valid email addresses.
- Check your email provider's sending limits. Some providers restrict the number of emails per hour.

## Queue Jobs Stuck

If jobs appear to be stuck in the queue and not processing:

- Middleware Mode: Jobs only process during HTTP requests. If there is no user traffic, jobs will not run. Consider switching to Scheduler or Worker mode.
- Restart the queue worker: `php artisan queue:restart`.
- Check for deadlocked jobs by inspecting the `jobs` table for entries with very old `reserved_at` timestamps.
- Clear the queue and retry: `php artisan queue:flush` then `php artisan queue:retry all`.

## 419 Page Expired Error

The "419 Page Expired" error occurs when the CSRF token has expired or is missing:

- Refresh the page to get a new CSRF token.
- If this happens frequently, increase the session lifetime in `.env`: `SESSION_LIFETIME=240`.
- Ensure your `APP_URL` matches the actual domain you are accessing (including https://).
- Check that the `SESSION_DOMAIN` is set correctly in `.env`.
- Verify that the `storage/framework/sessions` directory is writable.

## Livewire Connection Issues

If Livewire components show "connection lost" or fail to update:

- Ensure your web server is configured to handle Livewire's AJAX requests at `/livewire/update`.
- If using Nginx, add the Livewire route to your configuration to prevent 404 errors on Livewire requests.
- Check for JavaScript errors in the browser console that might indicate asset loading failures.
- Verify that `APP_URL` is set correctly. Livewire uses this for its AJAX endpoint.
- If behind Cloudflare or a reverse proxy, ensure WebSocket/long-polling connections are not being blocked.

## SMTP Connection Failed

If sending emails via SMTP fails:

- Verify your SMTP host, port, username, and password in the email account settings.
- Common SMTP ports: 587 (TLS), 465 (SSL), 25 (unencrypted, often blocked).
- Test the connection from the command line: `php artisan tinker` then `Mail::raw('Test', fn($m) => $m->to('test@example.com'))`.
- Some hosting providers block outbound SMTP. Contact your host to confirm port 587 is open.
- For Gmail SMTP, you may need to enable "Less secure apps" or use an App Password if 2FA is enabled on the Gmail account.

## High CPU on Shared Hosting

If you experience high CPU usage on shared hosting:

- Reduce the Middleware Mode job limit: set `max_jobs_per_request` to 1 or 2.
- Increase the throttle interval to 120 seconds or more.
- Reduce email sync frequency by setting a longer interval between sync jobs.
- Optimize the database: run `php artisan optimize` and ensure database indexes are in place.
- Enable application caching: `php artisan config:cache && php artisan route:cache && php artisan view:cache`.

## Contact Import Failing

If CSV contact import fails or produces errors:

- Ensure the CSV file uses UTF-8 encoding. Files exported from Excel may use a different encoding.
- The first row must contain column headers. Required columns: `email` (at minimum).
- Check the maximum file upload size in your PHP configuration: `upload_max_filesize` and `post_max_size`.
- For large imports (10,000+ contacts), increase the PHP memory limit: `memory_limit=512M`.
- Duplicate emails are skipped by default. Check the import summary for the count of skipped records.

## WhatsApp Webhook: "Signature did not match"

If `laravel.log` keeps logging "WhatsApp webhook: Signature did not match any active integration secret" and inbound messages never arrive:

- You have not saved the App Secret in Settings → Channels → WhatsApp. This field is required — Meta signs every webhook with it.
- The App Secret lives in Meta Dev Portal → App Settings → Basic → App secret (click Show). It is not the same as the Access Token or the Verify Token.
- Paste it, save the integration, wait for the next inbound message. The log should now read "WhatsApp: Webhook payload received".

## Telegram: "can't parse entities: Unsupported start tag"

Sending a Telegram reply returns 400 with can't parse entities: Unsupported start tag "p":

- This is caused by the rich-text editor wrapping your text in `<p>…</p>`. Telegram's HTML parser does not accept `<p>`.
- MailTrixy now sanitizes outbound Telegram messages automatically (strips disallowed tags, converts paragraphs/`<br>` to real newlines). If you still see this error, the deployed code is older than the fix — pull the latest.
- Allowed Telegram tags: `<b>`, `<i>`, `<u>`, `<s>`, `<a>`, `<code>`, `<pre>`, `<blockquote>`.

## Telegram Compose: "Username @… not found"

If typing `@someone` in Compose → Telegram returns "Username … not found. The user must send /start":

- Telegram does not allow bots to message users who have not initiated contact. The user must send `/start` to your bot first.
- As soon as they send `/start`, MailTrixy saves them as a Contact with `last_name = "@username"`. Compose can then look them up.
- If the user sent `/start` but compose still fails, check `laravel.log` for "Telegram: /start registered user". If missing, the webhook is not resolving the bot token correctly — re-save the integration.

## Canned-Response Shortcut Does Not Open Dropdown

Typing `/hi` in the Compose or Reply editor and the dropdown never appears:

- Check that at least one canned response with a matching title, shortcut, or content exists at Inbox → Quick Replies.
- The rich-text editor wraps typed text in HTML. MailTrixy now strips tags before checking the prefix — if your deployment is older, update to pick up that fix.
- After typing `/`, you must type at least one additional character for the dropdown to filter.
- Clicks on a response insert its content by replacing the draft. Edit the inserted text as needed and Send.

## Auto-Reply Not Firing on Non-Email Channels

An incoming WhatsApp / Telegram / SMS / Slack message matches your rule but no reply is sent:

- Confirm the rule is active (toggle ON) and its channel is set to All or to the specific inbound channel.
- If match type is Exact phrase match, the customer's message (trimmed, lower-cased) must equal the keyword exactly. Switch to Contains if you want looser matching.
- If the rule has First message only enabled, it only fires on the very first inbound message in a conversation. Existing threads won't trigger.
- Check `laravel.log` for "AutoReply: Keyword rule matched" followed by the channel's send log ("WhatsApp: Text message sent", "Telegram: Message sent", etc.). If the match log is present but the send fails, the error is in `delivery_error` on the outbound message row.
- If no match log appears at all, the incoming message body may be HTML-wrapped or empty. The service strips HTML before matching; verify the message has plain-text content.