Skip to content
This page was generated and translated with the assistance of AI. If you spot any inaccuracies, feel free to help improve it. Edit on GitHub

Troubleshooting ​

This page covers the most common issues encountered when running PRX-Email, along with their causes and solutions.

OAuth Token Expired ​

Symptoms: Operations fail with Provider error code and a message about expired tokens.

Possible Causes:

  • OAuth access token has expired and no refresh provider is configured
  • The *_OAUTH_EXPIRES_AT environment variable contains a stale timestamp
  • The refresh provider is returning errors

Solutions:

  1. Verify token expiry timestamps:
bash
echo $PRX_EMAIL_IMAP_OAUTH_EXPIRES_AT
echo $PRX_EMAIL_SMTP_OAUTH_EXPIRES_AT
# These should be Unix timestamps in the future
  1. Manually reload tokens from environment:
rust
// Set fresh tokens
std::env::set_var("PRX_EMAIL_IMAP_OAUTH_TOKEN", "new-token");
std::env::set_var("PRX_EMAIL_SMTP_OAUTH_TOKEN", "new-token");

// Reload
plugin.reload_auth_from_env("PRX_EMAIL");
  1. Implement a refresh provider for automatic token renewal:
rust
let plugin = EmailPlugin::new_with_config(repo, config)
    .with_refresh_provider(Box::new(my_refresh_provider));
  1. Re-run the Outlook bootstrap script to get fresh tokens:
bash
CLIENT_ID='...' TENANT='...' REDIRECT_URI='...' \
./scripts/outlook_oauth_bootstrap.sh

TIP

PRX-Email attempts to refresh tokens 60 seconds before they expire. If your tokens expire faster than your sync interval, ensure the refresh provider is connected.

IMAP Sync Fails ​

Symptoms: sync() returns a Network error, or the sync runner reports failures.

Possible Causes:

  • Incorrect IMAP server hostname or port
  • Network connectivity issues
  • Authentication failure (wrong password or expired OAuth token)
  • IMAP server rate limiting

Solutions:

  1. Verify connectivity to the IMAP server:
bash
openssl s_client -connect imap.example.com:993 -quiet
  1. Check the transport configuration:
rust
// Ensure host and port are correct
println!("IMAP host: {}", config.imap.host);
println!("IMAP port: {}", config.imap.port);
  1. Verify authentication mode:
rust
// Must have exactly one set
assert!(config.imap.auth.password.is_some() ^ config.imap.auth.oauth_token.is_some());
  1. Check sync runner backoff state. After repeated failures, the scheduler applies exponential backoff. Temporarily reset by using a far-future now_ts:
rust
let report = plugin.run_sync_runner(&jobs, now + 86400, &config);
  1. Check structured logs for detailed error information:
bash
# Look for sync-related structured logs
grep "prx_email.*sync" /path/to/logs

SMTP Send Fails ​

Symptoms: send() returns an ApiResponse with ok: false and a Network or Provider error.

Possible Causes:

  • Incorrect SMTP server hostname or port
  • Authentication failure
  • Recipient address rejected by the provider
  • Rate limiting or sending quota exceeded

Solutions:

  1. Check the outbox status:
rust
let outbox = plugin.get_outbox(outbox_id)?;
if let Some(msg) = outbox {
    println!("Status: {}", msg.status);
    println!("Retries: {}", msg.retries);
    println!("Last error: {:?}", msg.last_error);
    println!("Next attempt: {}", msg.next_attempt_at);
}
  1. Verify SMTP configuration:
rust
// Check auth mode
println!("Auth: password={}, oauth={}",
    config.smtp.auth.password.is_some(),
    config.smtp.auth.oauth_token.is_some());
  1. Check for validation errors. The send API rejects:

    • Empty to, subject, or body_text
    • Disabled email_send feature flag
    • Invalid email addresses
  2. Test with simulated failure to verify your error handling:

rust
use prx_email::plugin::SendFailureMode;

let response = plugin.send(SendEmailRequest {
    // ... fields ...
    failure_mode: Some(SendFailureMode::Network), // Simulate failure
});

Outbox Stuck in "sending" State ​

Symptoms: Outbox records have status = 'sending' but the process crashed before finalization.

Cause: The process crashed after claiming the outbox record but before finalizing it as sent or failed.

Solution: Manually recover stuck records via SQL:

sql
-- Identify stuck rows (threshold: 15 minutes)
SELECT id, account_id, updated_at
FROM outbox
WHERE status = 'sending' AND updated_at < strftime('%s','now') - 900;

-- Recover to failed and schedule retry
UPDATE outbox
SET status = 'failed',
    last_error = 'recovered_from_stuck_sending',
    next_attempt_at = strftime('%s','now') + 30,
    updated_at = strftime('%s','now')
WHERE status = 'sending' AND updated_at < strftime('%s','now') - 900;

Attachment Rejected ​

Symptoms: Send fails with "attachment exceeds size limit" or "attachment content type is not allowed".

Solutions:

  1. Check the attachment policy:
rust
let policy = &config.attachment_policy;
println!("Max size: {} bytes", policy.max_size_bytes);
println!("Allowed types: {:?}", policy.allowed_content_types);
  1. Verify the file size is within the limit (default: 25 MiB).

  2. Add the MIME type to the allowed list if it is safe:

rust
policy.allowed_content_types.insert("application/vnd.ms-excel".to_string());
  1. For path-based attachments, ensure the file path is under the configured attachment storage root. Paths containing ../ or symlinks that resolve outside the root are rejected.

Feature Disabled Error ​

Symptoms: Operations return FeatureDisabled error code.

Cause: The feature flag for the requested operation is not enabled for the account.

Solution:

rust
// Check current state
let enabled = plugin.is_feature_enabled(account_id, "email_send")?;
println!("email_send enabled: {}", enabled);

// Enable the feature
plugin.set_account_feature(account_id, "email_send", true, now)?;

// Or set the global default
plugin.set_feature_default("email_send", true, now)?;

SQLite Database Errors ​

Symptoms: Operations fail with Storage error code.

Possible Causes:

  • Database file is locked by another process
  • Disk is full
  • Database file is corrupted
  • Migrations have not been run

Solutions:

  1. Run migrations:
rust
let store = EmailStore::open("./email.db")?;
store.migrate()?;
  1. Check for locked database. Only one write connection can be active at a time. Increase the busy timeout:
rust
let config = StoreConfig {
    busy_timeout_ms: 30_000, // 30 seconds
    ..StoreConfig::default()
};
  1. Check disk space:
bash
df -h .
  1. Repair or recreate if the database is corrupted:
bash
# Back up the existing database
cp email.db email.db.bak

# Check integrity
sqlite3 email.db "PRAGMA integrity_check;"

# If corrupt, export and reimport
sqlite3 email.db ".dump" | sqlite3 email_new.db

WASM Plugin Issues ​

Network Guard Error ​

Symptoms: WASM-hosted email operations return EMAIL_NETWORK_GUARD error.

Cause: The network safety switch is not enabled.

Solution:

bash
export PRX_EMAIL_ENABLE_REAL_NETWORK=1

Host Capability Unavailable ​

Symptoms: Operations return EMAIL_HOST_CAPABILITY_UNAVAILABLE.

Cause: The host runtime does not provide the email capability. This occurs when running outside the WASM context.

Solution: Ensure the PRX runtime is configured to provide email host-calls to the plugin.

Sync Runner Keeps Skipping Jobs ​

Symptoms: The sync runner reports attempted: 0 even though jobs are configured.

Cause: All jobs are in backoff due to previous failures.

Solutions:

  1. Check the failure backoff state by examining structured logs.

  2. Verify network reachability and IMAP authentication before re-running.

  3. Reset backoff by using a far-future timestamp:

rust
let report = plugin.run_sync_runner(&jobs, now + 86400, &default_config);

High Send Failure Rate ​

Symptoms: Metrics show a high send_failures count.

Solutions:

  1. Inspect structured logs filtered by run_id and error_code:
bash
grep "prx_email.*send_failed" /path/to/logs
  1. Check SMTP auth mode. Ensure exactly one of password or oauth_token is set.

  2. Validate provider availability before enabling broad rollout.

  3. Check metrics:

rust
let metrics = plugin.metrics_snapshot();
println!("Send failures: {}", metrics.send_failures);
println!("Retry count: {}", metrics.retry_count);

Getting Help ​

If none of the above solutions resolve your issue:

  1. Check existing issues: github.com/openprx/prx_email/issues
  2. File a new issue with:
    • PRX-Email version (check Cargo.toml)
    • Rust toolchain version (rustc --version)
    • Relevant structured log output
    • Steps to reproduce

Next Steps ​

Released under the Apache-2.0 License.