> ## Documentation Index
> Fetch the complete documentation index at: https://gowa-docs.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Error Handling

> GOWA's response envelope, error codes and HTTP statuses, and how to handle them.

## Response envelope

Every JSON response has the same shape:

```json theme={null}
{
  "code": "SUCCESS",
  "message": "Success",
  "results": { }
}
```

| Field | Description |
| - | - |
| `code` | `SUCCESS` or an error code. Use this, together with the HTTP status, to branch. |
| `message` | Human-readable detail. Wording can change between releases; do not parse it. |
| `results` | Payload on success. Usually omitted on errors. |

## Error codes

| Code | HTTP | Meaning | What to do |
| - | - | - | - |
| `VALIDATION_ERROR` | 400 | Missing or invalid field. `message` names it. | Fix the request. |
| `INVALID_JID` | 400 | The number is not on WhatsApp, or the JID is malformed. | Check the number; do not retry. |
| `ALREADY_LOGGED_IN` | 400 | Login requested for a device that is already logged in. | Nothing, or log out first. |
| `DEVICE_ID_REQUIRED` | 400 | No device given and the server has zero or several devices. | Send `X-Device-Id`. |
| `AUTHENTICATION_ERROR` | 401 | The device is not logged in to WhatsApp (or WhatsApp authentication failed). | Check `/devices/{id}/status`; pair again if needed. |
| `NOT_FOUND` | 404 | Resource not found (message, chat, schedule…). | Check the ID. |
| `DEVICE_NOT_FOUND` | 404 | `X-Device-Id` names an unknown device. | Check the slot ID or JID. |
| `CONTEXT_ERROR` | 408 | The operation was cancelled or timed out internally. | Retry once. |
| `WA_REACHOUT_TIMELOCK` | 429 | WhatsApp temporarily blocks new chats from this account. | Stop outreach; see [Sending limits](https://gowa-docs.mintlify.site/advanced/rate-limiting.md). |
| `INTERNAL_SERVER_ERROR` | 500 | Unexpected failure, including recovered panics. | Retry with backoff; check logs. |
| `QR_CHANNEL_ERROR` | 500 | Could not obtain a QR code. | Retry login. |
| `SESSION_SAVED_ERROR` | 500 | Could not persist the session. | Check storage and permissions. |
| `UPLOAD_MEDIA_ERROR` | 500 | Media upload to WhatsApp failed. | Retry; check size and format. |
| `WEBHOOK_ERROR` | 500 | Webhook configuration or delivery error. | Check the webhook URL. |
| `INVALID_WA_CLI` | 500 | The device's WhatsApp client is not initialized. | Reconnect or log in again. |
| `DEVICE_MANAGER_UNAVAILABLE` | 503 | The device manager is not initialized. | Retry shortly; check startup logs. |
| `GATEWAY_TIMEOUT` | 504 | The request exceeded 45 seconds. | The action may still complete; check before retrying. |

Basic Auth failures return `401` from the auth layer before reaching GOWA's handlers; treat any `401` without a GOWA `code` as bad credentials.

## Retrying safely

Sending is **not idempotent**: GOWA has no idempotency key. If a send times out (`504`, network error), the message may already be delivered. Before resending, check the chat with `GET /chat/{chat_jid}/messages?is_from_me=true&limit=5`, or accept the risk of a duplicate.

| Retry | Do not retry |
| - | - |
| `500 INTERNAL_SERVER_ERROR`, `UPLOAD_MEDIA_ERROR`, `CONTEXT_ERROR`, `503` | `400` errors, `INVALID_JID`, `404`, `401`, `WA_REACHOUT_TIMELOCK` |

Use exponential backoff with a cap, and limit attempts.

```javascript theme={null}
async function gowa(path, body, attempt = 1) {
  const res = await fetch(`http://localhost:3000${path}`, {
    method: "POST",
    headers: {
      Authorization: "Basic " + Buffer.from("admin:changeme").toString("base64"),
      "Content-Type": "application/json",
      "X-Device-Id": "my-device",
    },
    body: JSON.stringify(body),
  });
  const data = await res.json().catch(() => ({}));
  if (res.ok) return data.results;

  const retryable = [500, 503].includes(res.status) && data.code !== "INVALID_JID";
  if (retryable && attempt < 4) {
    await new Promise((r) => setTimeout(r, 1000 * 2 ** attempt));
    return gowa(path, body, attempt + 1);
  }
  throw Object.assign(new Error(data.message || res.statusText), { code: data.code, status: res.status });
}
```

## Debugging

* Run with `APP_DEBUG=true` for detailed logs.
* Check the device: `GET /devices/{device_id}/status` (`is_connected`, `is_logged_in`).
* `GET /health` only confirms the process and database are up; it does not check WhatsApp connectivity.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.