Skip to content

Troubleshooting

Manaa-Soft edited this page Oct 22, 2026 · 9 revisions

Troubleshooting

Common Issues

"Password not found for WhatsApp Account X token"

Cause: frappe_whatsapp's wildcard doc_events hook fires send_template_message() on an OpenWA account where the token field is empty.

Fix: Ensure openwa_bridge is installed and overrides are active. The override resolves the account before checking _is_openwa_account() and skips the Meta token read when the account is OpenWA.


Messages Not Sending

  1. Verify OpenWA session is connected: check the OpenWA dashboard
  2. Confirm OpenWA Enabled is checked on the WhatsApp Account
  3. Check the Frappe error log for API response details
  4. Verify the API key matches between OpenWA and Frappe
  5. Check circuit breaker status — if tripped, wait for cooldown or reset

Send Fails with 409 or 422 (Idempotency)

The bridge sends every outbound request with an Idempotency-Key header derived from the WhatsApp Message name (frappe-{name}, or frappe-{name}-hdr for the dynamic header image). OpenWA enforces uniqueness on (sessionId, idempotencyKey) for 24 hours, independent of route.

Error Meaning Fix
409 IDEMPOTENCY_KEY_IN_PROGRESS The original request with this key is still running Wait and let the outbox worker retry; do not resend under a new key
409 IDEMPOTENCY_OUTCOME_UNKNOWN OpenWA lost track of the result (e.g. restart mid-send) Check the message in WhatsApp before retrying — resending may duplicate
422 IDEMPOTENCY_KEY_REUSED The same key was used with a different payload The key is derived from the document name and cannot be edited. Create a new WhatsApp Message (new name → new key) instead of re-sending changed content under the old doc

The error text shown in OpenWA API Error is parsed from error.message / error.code (and top-level message/code), so the code above appears verbatim. A replayed request returns Idempotent-Replayed: true and the original response.


message.receipt Not Arriving

message.receipt carries per-participant delivered / read / played status and is never delivered through a * wildcard. To receive it:

  1. Subscribe to message.receipt explicitly on the OpenWA webhook (or add it to the account's Webhook Events field and save).
  2. Confirm OpenWA is running the Baileys engine — other engines do not emit it.
  3. Check OpenWA Event Log for message.receipt rows; the handler requires both a message id (messageId or id) and a participant, and silently drops events missing either.

Note this is separate from message.ack, which reports your own outbound message's delivery/read state.


Template Sending Fails with 400

  1. Check if the template exists in OpenWA — the bridge auto-recovers stale IDs, but verify the OpenWA Template ID field is populated
  2. Verify the Fields child table on the notification maps correctly to document field names
  3. Check the OpenWA API Error log entry for the full response body

Dynamic Image Header Not Working

  1. Verify PyMuPDF is installed: bench pip install PyMuPDF
  2. Check that the Print Format for Header field is set on the template
  3. Verify the Dynamic Image Header checkbox is checked
  4. If you want letterhead: Include Letter Head must be on AND Letter Head for Image must be selected
  5. Look for "OpenWA: Dynamic header image failed" in error logs
  6. OpenWA's send-image endpoint requires mimetype: "image/png" in the payload
  7. Both Template and Jinja send types support dynamic image headers

Setup Timeout / "Cannot Connect to OpenWA"

  1. Verify OpenWA is running: curl http://localhost:2785/api/sessions
  2. If OpenWA is down, restart it: cd ~/OpenWA && pm2 restart openwa-gateway
  3. The setup flow has increased timeouts (30s for API calls, 60s for session start)
  4. Check that openwa_base_url matches your OpenWA address

Inbound Messages Not Received

  1. Test the HMAC signature: the X-Openwa-Signature header must match sha256=<hex>
  2. Check the webhook URL is accessible from OpenWA
  3. Verify SSRF_ALLOWED_HOSTS includes your Frappe site name, server IP, and MinIO (e.g., SSRF_ALLOWED_HOSTS=your-site-name,your-server-ip,localhost,127.0.0.1,minio)
    • your-site-name = your Frappe/ERPNext site name (e.g., my-site)
    • your-server-ip = the IP used to connect from VM to host (e.g., 192.168.1.15)
    • minio = MinIO service hostname (if used for file storage)
  4. Review OpenWA webhook logs for delivery status
  5. Check the webhook's event list: the account's Webhook Events field wins when set; otherwise the bridge syncs its 17-event default (message.edited, status.received, group and call events included). A blank field on a new account starts from the 11-event fixture default — message.receipt is in that list but not in the 17-event fallback, so add it explicitly if you rely on receipts.

Sessions Don't Reconnect After VM Restart

  1. Enable Frappe scheduler: bench --site your-site scheduler enable
  2. Verify scheduler is running: bench --site your-site doctor
  3. Check OpenWA is running: curl http://localhost:2785/api/sessions
  4. The pre-send check auto-restarts sessions on next message attempt
  5. If session shows "requires QR re-scan", open WhatsApp Account form and scan again

Session Connects Then Disconnects After 1-2 Minutes

Symptoms: Session connects successfully, shows "Connected" for a few minutes, then drops to "Failed" or "Disconnected".

Root cause: OpenWA auto-downloads media from all incoming messages. When reconnecting after a brief disconnection, WhatsApp Web queues messages — the catch-up flood of media downloads overwhelms Chrome/Puppeteer and crashes the session.

Fix:

  1. Disable media pre-download in OpenWA (our bridge handles media on-demand via webhooks):
# Edit OpenWA .env
nano ~/OpenWA/.env

# Add these lines:
MEDIA_DOWNLOAD_ENABLED=false
STORE_EPHEMERAL_MESSAGES=false

# Delete the generated env that overrides your settings
rm -f ~/OpenWA/data/.env.generated

# Restart OpenWA
pm2 restart openwa-gateway
  1. Verify the Frappe scheduler is running (health check needs it):
bench --site your-site scheduler enable
bench --site your-site doctor
  1. Check server resources (Chrome needs ~300MB RAM per session):
free -h
  1. Test internet connectivity to WhatsApp:
curl -I https://web.whatsapp.com --connect-timeout 5

Recovery: The health check (hourly) now automatically handles failed sessions:

  • Tries force-kill first (keeps auth, no QR re-scan needed)
  • If still failed, deletes and recreates the session (QR re-scan required)

Session Shows "Failed" / "Corrupted" / "Execution Context Destroyed"

Cause: Chrome/Puppeteer crash in OpenWA (browser session died), or session got stuck.

Fix (automatic):

  1. The form shows "Session Failed — Reconnect to Fix" indicator
  2. Click "Reconnect" → confirms
  3. The app first tries force-kill (SIGKILLs the crashed Chrome process, keeps session data — no QR re-scan needed)
  4. If force-kill doesn't help, it deletes and recreates the session (QR re-scan required)

Fix (manual, if automatic doesn't work):

  1. In OpenWA dashboard (localhost:2785/sessions), click "Kill Stuck" on the session
  2. Then click "Reconnect" — this restarts the session with existing auth data
  3. If that fails too, click "Delete" in OpenWA, then clear the OpenWA Session ID in Frappe and click "Setup OpenWA"

"Session Deleted — Re-setup Required" After Deleting Session from OpenWA Dashboard

Cause: You deleted the session from OpenWA's dashboard (localhost:2785/sessions), but the WhatsApp Account in Frappe still holds the old session UUID.

Fix:

  1. The form now detects this automatically and shows a "Setup OpenWA (New Session)" button
  2. Click it — it clears the stale session ID and reloads the form
  3. Click "Setup OpenWA" to create and connect a new session

If the button doesn't appear, you can manually clear the session ID:

  1. Open the WhatsApp Account in Frappe Desk
  2. Clear the OpenWA Session ID field
  3. Save
  4. Click "Setup OpenWA"

Duplicate Messages Being Sent

Cause: Notification flow with dynamic header was sending both image and text as separate messages.

Fix: This was a known bug that has been fixed. Update to the latest version.


Template Translation Errors

The app includes fallback logic — if frappe.get_doc() fails, it falls back to frappe.db.get_value(). Check the error log if templates aren't rendering correctly.


Outbox Entries Stuck as "Pending"

  1. Check if the background worker is running: bench --site your-site doctor
  2. Check for circuit breaker trips in error logs
  3. Verify the scheduler is running for the safety-net sweep
  4. Check Redis connectivity (used for distributed locks)

Rate Limit Exceeded Errors

The inbound webhook has rate limiting (default 60 req/min per IP). If OpenWA is sending too many retries:

  1. Increase the rate limit in WhatsApp Account Settings
  2. Or check why OpenWA is retrying excessively (may indicate a processing issue)

Template Sync Fails (409 Conflict)

This means a template with the same name already exists in OpenWA. The bridge handles this automatically:

  • If the template ID is stale, it looks up by name and updates
  • If no match, it creates a fresh template

If issues persist, try deleting the template from the OpenWA dashboard and re-saving in Frappe.


@lid JID Format Not Handled

The bridge supports @lid JID suffixes (WhatsApp LID format). If you see unknown sender IDs, check that strip_jid_suffix() in utils.py includes @lid.


Media Replies Not Natively Supported (Workaround Active)

Cause: OpenWA POST /messages/reply is text-only — ReplyMessageDto = chatId + quotedMessageId + text. The send routes (send-image, send-video, send-audio, send-document) accept no quote parameter.

Workaround: When a media message is sent with is_reply:

  1. Send the media unquoted via send-{type} (with link or base64 payload).
  2. Follow up with a text reply quoting the just-sent media messageId.

Both messages are delivered; the WhatsApp Message doc tracks the media messageId. A failure of the follow-up reply is logged (OpenWA Media-Reply Workaround Failed) but does not fail the send.


Webhook DLQ Requires ADMIN Role

Cause: OpenWA GET /api/webhooks/delivery-failures only works with an ADMIN-role API key. Non-ADMIN keys get 403 (expected — skipped silently).

Note: The bridge reconciles missed inbound via replay_webhooks() instead, and the daily check_webhook_delivery_failures() writes webhook.delivery_failure rows to the OpenWA Event Log when an ADMIN key is available.


Server Environment

Item Value
Server IP 192.168.1.15
OpenWA API http://localhost:2785
OpenWA Dashboard http://localhost:2886
Frappe bench ~/frappe-bench
Site your-site.local

Monitoring

Check Error Logs

Frappe Desk → Setup → Error Log
Filter by: "OpenWA"

Check OpenWA Logs

# PM2 logs
pm2 logs openwa-gateway

# Or check OpenWA dashboard at localhost:2886

Test OpenWA API Directly

# Health check
curl http://localhost:2785/api/sessions

# Send test text
curl -X POST http://localhost:2785/api/sessions/<sessionId>/messages/send-text \
  -H "Content-Type: application/json" \
  -H "X-API-Key: <api-key>" \
  -d '{"chatId": "967777715787@c.us", "text": "Test from API"}'

Check Scheduler

bench --site your-site doctor
bench --site your-site scheduler status

OpenWA Key Behaviors

  1. send-template is text-only: It renders header+body+footer → joins with \n\n → sends as plain text via sendText()
  2. Template vars are Record<string, string>: Keys must match {{key}} in the template body
  3. Template uniqueness: Names are unique per session (not globally)
  4. SSRF blocks private IPs: Must set SSRF_ALLOWED_HOSTS in OpenWA's .env — include your Frappe site name, server IP, and MinIO (e.g., SSRF_ALLOWED_HOSTS=your-site-name,your-server-ip,localhost,127.0.0.1,minio)
    • your-site-name = your Frappe/ERPNext site name (e.g., my-site)
    • your-server-ip = the IP used to connect from VM to host (e.g., 192.168.1.15)
    • minio = MinIO service hostname (if used for file storage)
  5. Webhook signature header: X-Openwa-Signature (lowercase 'wa')
  6. HMAC format: sha256=<hex-digest> of HMAC-SHA256(secret, raw_body)

Clone this wiki locally