Account Troubleshooting
This guide covers common issues when working with email accounts in EmailEngine and how to resolve them.
Quick Diagnostics
Check Account Status
First, check the account's current state using the Get Account API endpoint:
curl https://emailengine.example.com/v1/account/user123 \
-H "Authorization: Bearer YOUR_TOKEN"
Look for:
state: Current account state, one of the states listed under Account stateslastError: The last error, with the provider's rejection inlastError.responsesyncTime: Last successful sync (IMAP accounts)authFailureDisabledAt: Set when EmailEngine itself switched syncing off after repeated authentication failures,nullotherwise (since v2.79.4)imap.disabled:truewhen syncing is off, whether the operator or the safety net turned it off
Check EmailEngine Logs
EmailEngine logs contain detailed error information:
# If running with systemd
journalctl -u emailengine -f
# If running with Docker
docker logs -f emailengine
# If running manually (logs go to stdout)
# EmailEngine uses pino for JSON logging to stdout
Common Account States and Solutions
State: authenticationError
What it means: Invalid or expired credentials.
For IMAP/SMTP Accounts
Common Causes:
-
Incorrect password
- Password was changed on the email provider
- Typo in password
- Wrong username
Solution:
curl -X PUT https://emailengine.example.com/v1/account/user123 \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"imap": { "partial": true, "disabled": false, "auth": { "user": "user@example.com", "pass": "correct-password" } },
"smtp": { "partial": true, "auth": { "user": "user@example.com", "pass": "correct-password" } }
}'
# Then reconnect using the Reconnect Account API endpoint
curl -X PUT https://emailengine.example.com/v1/account/user123/reconnect \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"reconnect": true}'"disabled": falsematters when the failures have been going on for more than three days: by then EmailEngine has switched syncing off. In v2.79.5 a partial update that changesimap.authlifts the switch-off on its own, but in v2.79.4 it does not, and the explicit flag is harmless in either case. -
App password required but not used
- Gmail: Account passwords completely disabled, app-specific passwords required for all accounts
- Yahoo, iCloud: App-specific passwords required if 2FA is enabled
Solution:
- Generate app-specific password from provider
- Update account with app password instead of main password
- Gmail app passwords (requires 2FA enabled)
- Yahoo app passwords
- iCloud app passwords
-
Password authentication disabled (Gmail)
- Gmail has completely disabled account password authentication for all accounts
- The "Less secure app access" feature is no longer available
Solution:
- Use app-specific passwords (requires 2FA): Gmail app passwords
- Or switch to OAuth2: Gmail OAuth2 guide
-
IMAP/SMTP disabled (Microsoft 365)
- Admin may have disabled IMAP/SMTP protocols
Solution:
- Enable in Microsoft 365 admin center
- Navigate to Users > Active users > Mail settings
- Enable IMAP and SMTP AUTH
- Or use MS Graph API: Outlook setup guide
For OAuth2 Accounts
Common Causes:
-
Access token expired and refresh failed
- Refresh token may be invalid
- OAuth2 app credentials changed
- User revoked access
Solution:
- Have user re-authenticate via hosted authentication form
- Generate new authentication form URL:
curl -X POST https://emailengine.example.com/v1/authentication/form \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"account": "user123",
"email": "user@gmail.com",
"redirectUrl": "https://myapp.com/settings"
}' -
OAuth2 app misconfigured
- Client ID or secret incorrect
- Redirect URL mismatch
- Required scopes not configured
Solution:
- Open the app under Integrations > OAuth2 Apps. An app whose credentials the provider has rejected is badged Failing and carries the provider's message at the top of its page
- Press Verify setup on the app page, or call
POST /v1/oauth2/{app}/verify, to walk the authentication chain step by step. See Verifying the app setup - Check the client ID, secret and redirect URL against Google Cloud Console or Microsoft Entra
- Gmail OAuth2 setup guide
- Outlook OAuth2 setup guide
-
Insufficient permissions
- Account doesn't have required scopes
- For shared mailboxes: user doesn't have access
Solution:
- Update OAuth2 app scopes
- Have user re-authenticate to grant new scopes
- For shared mailboxes: verify user has permissions in Microsoft 365 admin
State: connectError
What it means: Cannot reach the mail server.
Common Causes:
-
Incorrect host or port
# Check current settings
curl https://emailengine.example.com/v1/account/user123 \
-H "Authorization: Bearer YOUR_TOKEN" | jq '.imap'Solution:
- Verify IMAP/SMTP settings with provider documentation
- Common Gmail settings: Gmail IMAP
- Common Outlook settings: Outlook IMAP
-
Firewall blocking connections
- EmailEngine server firewall blocks outbound connections
- Corporate firewall blocks email ports
Solution:
# Test connectivity from EmailEngine server
telnet imap.gmail.com 993
telnet smtp.gmail.com 587If connection fails, check firewall rules:
# Allow outbound connections to IMAP/SMTP ports
iptables -A OUTPUT -p tcp --dport 993 -j ACCEPT # IMAP SSL
iptables -A OUTPUT -p tcp --dport 587 -j ACCEPT # SMTP STARTTLS
iptables -A OUTPUT -p tcp --dport 465 -j ACCEPT # SMTP SSL -
DNS resolution failure
- Cannot resolve mail server hostname
Solution:
# Test DNS resolution
nslookup imap.gmail.com
dig imap.gmail.comIf DNS fails:
- Check
/etc/resolv.conf - Verify network configuration
- Try different DNS server (e.g., 8.8.8.8)
-
Server is down or unreachable
- Mail provider having outage
- Server maintenance
Solution:
- Check provider status page
- Wait and retry later
- EmailEngine will automatically retry
-
The server accepted the connection but could not serve the login
- An IMAP server can answer
NOto a login for a reason that is its own rather than the credential's. Since v2.80.1 EmailEngine reports such a login asconnectError, notauthenticationError: aNOcarrying the RFC 5530 response code[UNAVAILABLE],[SERVERBUG],[INUSE]or[LIMIT], and Exchange Online's codelessUser is authenticated but not connected., which is its front end failing to reach the mailbox behind an accepted token - The account sends one
connectErrorwebhook carrying the server's text, keeps reconnecting on the normal schedule, and does not reportauthenticationSuccesswhen the login goes through again. A repeated identical failure is announced once rather than on every retry - For Exchange Online the same text is also the permanent answer when IMAP is disabled for the mailbox, see IMAP Not Enabled
Solution:
- Check
lastError.responsefor the server's text, then the provider's status page - If it persists on an Exchange Online mailbox, check that IMAP is enabled for it
- An IMAP server can answer
State: connecting
What it means: Connection in progress.
Normal Behavior:
- A reconnect passes through this state in seconds
- The first connection takes longer, because the folder list is fetched and the initial sync starts
If Stuck:
-
Check logs for what's happening:
journalctl -u emailengine -f | grep user123 -
Possible issues:
- Slow server response
- Large mailbox syncing
- Network latency
-
Wait a few minutes before intervening
-
If stuck >5 minutes:
# Trigger reconnection
curl -X PUT https://emailengine.example.com/v1/account/user123/reconnect \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"reconnect": true}'
State: unset
What it means: The account is not syncing. Either no IMAP or OAuth2 configuration is set, or syncing was switched off: by the operator through imap.disabled, or by EmailEngine itself after repeated authentication failures.
Tell the cases apart with the account object:
imap.disabled | authFailureDisabledAt | Cause |
|---|---|---|
false or absent | null | No credentials yet. Typically an account created through the hosted authentication form whose user has not completed the OAuth2 consent |
true | null | The operator switched syncing off (a send-only account). A password account switched off by a release older than v2.79.4 also looks like this, since the timestamp did not exist yet; lastError then records the authentication failures behind it |
true | a timestamp | EmailEngine switched syncing off after the account had failed authentication for longer than EENGINE_MAX_IMAP_AUTH_FAILURE_TIME, three days by default. lastError holds the provider's last rejection |
No credentials yet:
- The user needs to open the authentication form URL and complete the consent
- If they say they did: check the redirect URL, confirm the OAuth2 app is enabled, look at the app's page for a recorded error, and generate a fresh form URL
Switched off by the operator:
- Set
imap.disabledtofalse, as in the reconnect example under disconnected
Switched off after authentication failures (since v2.79.4 the account page shows a "Syncing was switched off" alert with the time, and the accounts list badges it "Syncing switched off"):
- Fix the credentials first. A re-authorization through the hosted authentication form or the account page's Re-authenticate button lifts the disable and reconnects the account; so does a
PUT /v1/account/{account}that carries new OAuth2 tokens, replaces the wholeimapobject, or sets"disabled": false. In v2.79.5 a partial update ofimap.authlifts it too, and saving the account's edit form with new IMAP credentials does the same - Or press Resume syncing on the account page to retry with the stored credentials. This is the only admin path for a Gmail API or MS Graph account, whose edit page has no IMAP settings
- A shared mailbox that borrows another account's token is not switched off in v2.79.5; fix the account that owns the credential and the shared mailboxes come back with it
PUT /v1/account/{account}/reconnectdoes not help here: it answers{"reconnect": false}and changes nothing, because the connection setup checks the flag before dialing out
The full list of what lifts the disable, and the version history behind it, is in Accounts switched off after authentication failures.
State: disconnected
What it means: The connection dropped and EmailEngine is retrying with backoff. This is transient; an account that stays here is usually one whose server keeps closing the session, so check lastError and the per-account log.
A disabled account does not report disconnected; it reports unset (above). To re-enable one the operator switched off:
# Re-enable the account
curl -X PUT https://emailengine.example.com/v1/account/user123 \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "imap": { "partial": true, "disabled": false } }'
# Then reconnect
curl -X PUT https://emailengine.example.com/v1/account/user123/reconnect \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"reconnect": true}'
State: paused
What it means: Syncing was paused through the API and no connection is maintained. EmailEngine pauses an account while PUT /v1/account/{account}/flush clears its stored mailbox data, and reconnects it when the flush completes. An account that stays paused after a flush has finished can be brought back with a reconnect.
Provider-Specific Issues
Gmail Issues
Account Password Authentication No Longer Supported
Error Message: "Please log in via your web browser" or "Invalid credentials"
Gmail has completely disabled account password authentication. The "Less secure app access" feature is no longer available. You must use app passwords or OAuth2.
Solution:
- App Passwords (for testing): Generate an app-specific password (requires 2FA enabled)
- OAuth2 (recommended for production): Follow the Gmail OAuth2 guide
Rate Limits
Symptoms:
- Intermittent connection failures
- Slow syncing
- Temporary authentication errors
Gmail Limits:
- 15 concurrent IMAP connections
- 2500 MB download/day
- 500 MB upload/day
Solution:
- Reduce sub-connections
- Implement path filtering
- Consider Gmail API for high-volume: Gmail API guide
- Spread operations over time
OAuth2 Scope Too Wide (Public Apps)
Error: Google rejects your OAuth2 app verification
Reason: https://mail.google.com/ scope too broad
Solution:
- Use narrower scopes if possible
- Justify why IMAP access is needed
- Consider Internal app (organization only)
- Consider app passwords as alternative
Outlook/Microsoft 365 Issues
IMAP Not Enabled
Error Message: User is authenticated but not connected. Exchange Online answers the login with that text, without a response code, when IMAP is disabled for the mailbox. Since v2.80.1 the account reports it as connectError rather than authenticationError, because the credential was accepted.
Solution:
- Go to Microsoft 365 admin center
- Users > Active users > Select user
- Mail tab > Manage email apps
- Enable IMAP
- Wait 15-30 minutes for changes to propagate
OAuth2 redirect_uri Mismatch
Error Code: AADSTS50011
Solution:
- Check redirect URI in Azure AD app registration
- Must match exactly in EmailEngine OAuth2 settings
- Check for:
- http vs https
- Trailing slashes
- Port numbers
- Case sensitivity
Admin Consent Required
Error Message: Need admin approval
Solution:
- Organization admin must grant consent
- Or admin can pre-approve app for all users
- In Azure AD > App registrations > API permissions > Grant admin consent
Shared Mailbox Access Denied
Symptoms:
- Authentication succeeds but cannot access mailbox
- "Mailbox not found" error
Solution:
- Verify user has "Full Access" permission to shared mailbox
- In Microsoft 365 admin:
- Recipients > Shared > Select mailbox
- Mailbox delegation > Full Access
- Add user
- Wait 15-30 minutes for permissions to propagate
Yahoo/AOL/Verizon Issues
App Password Required
Error Message: "Invalid credentials"
Cause: 2FA enabled, app password needed
Solution:
- Generate app password:
- Yahoo: Account Security
- AOL: Account Security settings
- Use app password instead of main password
- Update account in EmailEngine
iCloud Issues
App-Specific Password Required
Error Message: "Invalid credentials"
Solution:
- Generate app-specific password:
- Visit appleid.apple.com
- Sign in > Security > App-Specific Passwords
- Generate password
- Use app-specific password in EmailEngine
Two-Factor Authentication Must Be Enabled
iCloud requires 2FA enabled to generate app-specific passwords.
Solution:
- Enable 2FA on Apple ID
- Then generate app-specific password
Webhook Issues
Webhooks that do not arrive, or arrive late, are a problem of the instance rather than of one account: see Webhooks Not Delivered and Webhooks Delayed on the general troubleshooting page. The one account-level check is the push subscription of an API account: a Microsoft Graph account carries outlookSubscription in its account object, and a Gmail API account reports its watch as gmailWatch, with the state of the Pub/Sub application itself in GET /v1/pubsub/status. See Watch state.
Connection Issues
Too Many Connections
Error Message: "Maximum number of connections reached"
Cause: Exceeding provider's concurrent connection limit
Solution:
# Check current subconnections
curl https://emailengine.example.com/v1/account/user123 \
-H "Authorization: Bearer YOUR_TOKEN" \
| jq '.subconnections'
# Reduce sub-connections
curl -X PUT https://emailengine.example.com/v1/account/user123 \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "subconnections": [] }'
Provider Limits:
- Gmail allows 15 simultaneous IMAP connections per account
- Other providers publish their own limits; the account's sync connection, each sub-connection and every IMAP proxy session all count against it
SSL/TLS Certificate Errors
Error Message: "Certificate verification failed" or "CERT_HAS_EXPIRED"
For Provider Servers:
Usually indicates provider issue or misconfigured server.
For Self-Hosted Servers:
# Accept self-signed certificates (development only)
curl -X PUT https://emailengine.example.com/v1/account/user123 \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"imap": {
"partial": true,
"tls": {
"rejectUnauthorized": false
}
}
}'
Only disable certificate verification for development/testing with self-signed certs. In production, use proper CA-signed certificates.
New Messages Stop Arriving While the Account Shows connected
Symptoms:
- The account stays
connectedandlastErroris empty - Messages in other folders are picked up by the periodic resync, but new messages in the main mailbox (usually INBOX) stop producing
messageNewwebhooks - A reconnect brings everything through at once
Cause: A server keeps the primary connection alive, answering IDLE, NOOP and the periodic folder checks, while its view of the selected mailbox stops moving. EmailEngine learns about new messages in the main mailbox only from that connection, and the periodic resync deliberately leaves the selected mailbox alone, so a frozen session looks healthy.
Solution: Since v2.82.0 EENGINE_IMAP_STALE_CHECK_INTERVAL turns on a check for this. Set it to a duration, for example 1h:
EENGINE_IMAP_STALE_CHECK_INTERVAL=1h
Once the next-UID counter the primary connection knows for the main mailbox has not moved for that long (with up to a quarter of the interval added as random jitter, so a fleet of accounts does not check at once), the periodic resync pass asks the server for the mailbox status over the command connection, and when the server's counter is ahead on two consecutive checks the primary connection is closed and reconnected. The pass runs every 15 minutes by default (imap.resyncDelay on the account). The check is off when the variable is unset. A reconnect it triggers costs one login and a fresh SELECT of the main mailbox; nothing is re-indexed.
IDLE Timeout Issues
Symptoms:
- The account reconnects every few minutes, at roughly the same interval each time
- The logs show the connection closing while IDLE was running, with no
BYEfrom the server
Cause: The server, or a proxy, load balancer or firewall in front of it, closes connections that have been idle for longer than its timeout. Some do this after a few minutes, well below the 30 minutes the IMAP standard asks for.
Solution: EmailEngine reconnects on its own and falls back to polling on servers without IDLE, so no messages are lost. To stop the reconnects, set the account's IDLE restart interval below the server's timeout. For a connection that drops after about four and a half minutes:
{
"imap": {
"partial": true,
"maxIdleTime": 120000
}
}
EmailEngine then restarts IDLE every two minutes, so the connection never looks idle. You can confirm the timeout first with a manual session (openssl s_client -connect imap.example.com:993 -quiet, then LOGIN, SELECT INBOX and IDLE) and note when the server closes it.
If the connection closes within seconds of logging in, before IDLE has had time to run, the problem is on the server side and a restart interval will not help; contact the provider.
Performance Issues
A slow first sync and high memory or CPU use are covered under Performance Issues on the general troubleshooting page, and sizing guidance is on Performance Tuning. The two levers that live on the account are path, which limits the folders that are indexed (Configure path filtering), and subconnections, each of which costs one more IMAP connection (Enable sub-connections).
OAuth2-Specific Issues
Token Refresh Failures
A refresh the provider rejects (invalid_grant in the logs) puts the account into authenticationError. The causes and the diagnostic steps are under Token Refresh Fails on the general troubleshooting page; a rotated or expired Microsoft client secret, which fails every account of that application at once, is covered on OAuth2 Token Management. Two rules apply to every cause:
- A token endpoint that answers 408, 429 or a 5xx is treated as unavailable rather than as a refusal: the attempt is retried and no webhook is sent (since v2.80.0).
- An account that keeps failing is switched off once the failures have run for
EENGINE_MAX_IMAP_AUTH_FAILURE_TIME, three days by default, so a dead grant is not retried against the provider forever. The account then reportsunsetwithauthFailureDisabledAtset. Re-authorizing lifts it (since v2.79.4; before that onlyimap.disabled: falsethrough the API did), and so does Resume syncing on the account page. Before v2.79.3 this only applied to password IMAP accounts.
"redirect_uri_mismatch" Error
Google Error Message: "The redirect URI in the request does not match..."
Microsoft Error Code: AADSTS50011
Solution:
- Check redirect URI in OAuth2 app configuration (Google Cloud Console / Azure AD)
- Check redirect URL in EmailEngine OAuth2 app settings
- They must match exactly:
- Case-sensitive
- http vs https
- Trailing slashes matter
- Port numbers must match
- Domain must match
Example mismatch:
- Provider:
https://ee.company.com/oauth - EmailEngine:
https://ee.company.com:3000/oauthThe port differs, so the provider rejects the redirect.
Insufficient Permissions
Error Message: "insufficient_scope" or "unauthorized_client"
Cause: Required scope not configured
Solution:
-
Check required scopes:
- Gmail IMAP:
https://mail.google.com/ - Gmail API:
gmail.modify - Outlook IMAP:
IMAP.AccessAsUser.All,SMTP.Send,offline_access - Outlook Graph:
Mail.ReadWrite,Mail.Send,offline_access
- Gmail IMAP:
-
Update OAuth2 app in provider console:
- Google Cloud Console > APIs & Services > OAuth consent screen > Scopes
- Azure AD > App registrations > API permissions
-
Update EmailEngine OAuth2 app if using additional scopes
-
Have users re-authenticate to grant new permissions
Diagnostic Commands
Check Account Details
# Full account info
curl https://emailengine.example.com/v1/account/user123 \
-H "Authorization: Bearer YOUR_TOKEN" \
| jq .
# Just the state
curl https://emailengine.example.com/v1/account/user123 \
-H "Authorization: Bearer YOUR_TOKEN" \
| jq -r .state
# Subconnections info
curl https://emailengine.example.com/v1/account/user123 \
-H "Authorization: Bearer YOUR_TOKEN" \
| jq '.subconnections'
Test IMAP Connection
# From EmailEngine server
openssl s_client -connect imap.gmail.com:993 -crlf
# Type: A LOGIN user@gmail.com password
# Then: B LIST "" "*"
Test SMTP Connection
# From EmailEngine server
openssl s_client -connect smtp.gmail.com:587 -starttls smtp -crlf
# Type: EHLO example.com
# Then: AUTH LOGIN
Check Network Connectivity
# Test DNS
dig imap.gmail.com
# Test port connectivity
telnet imap.gmail.com 993
nc -zv imap.gmail.com 993
# Check firewall rules
iptables -L OUTPUT -n
# Trace route
traceroute imap.gmail.com
Verify OAuth2 Token
# Get current token
curl https://emailengine.example.com/v1/account/user123/oauth-token \
-H "Authorization: Bearer YOUR_TOKEN"
# Test token with provider API (Gmail example)
TOKEN="ya29.a0AWY7..."
curl https://www.googleapis.com/gmail/v1/users/me/profile \
-H "Authorization: Bearer $TOKEN"
Getting Help
Information to Provide
When seeking help, include:
- EmailEngine version: The Software versions panel on the dashboard, which also lists the Node.js, Redis, ImapFlow and BullMQ versions, or
emailengine --version - Account state: From account details API
- Error messages: From logs
- Provider: Gmail, Outlook, Yahoo, etc.
- Authentication method: IMAP/SMTP, OAuth2, service account
- Reproduction steps: What leads to the issue
EmailEngine Logs
The per-account log is usually more useful than the server log, because it holds the protocol conversation for that one account. Turn it on for the account, reproduce the problem, then read it back:
curl -X PUT https://emailengine.example.com/v1/account/user123 \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"logs": true}'
curl "https://emailengine.example.com/v1/logs/user123" \
-H "Authorization: Bearer YOUR_TOKEN"
logs on an account is a boolean. Retention (logs.maxLogLines) and the switch that turns this on for every account (logs.all) are server-wide settings. See Per-account logs.
See Logging for the format and what each level records.
For the server log:
# Logs with account-specific filter
journalctl -u emailengine | grep user123
# Logs with OAuth2 filter
journalctl -u emailengine | grep -i oauth
# Logs with error filter
journalctl -u emailengine | grep -i error
# Follow logs in real-time
journalctl -u emailengine -f
Useful Resources
- EmailEngine API reference
- EmailEngine GitHub Issues
- Support Contact
- Provider-specific documentation:
See Also
- Managing accounts - Reconnecting, re-enabling, and rotating credentials
- Account types - Whether a different backend avoids the problem entirely
- Logging - Turning up detail on one account or on the whole server
- Troubleshooting - Problems that are not account-specific
- Support - What to send when you ask for help