Error codes
Every HTTP status and code the ingest service returns, and what to do about each.
An error response over HTTP is a JSON body of the shape { error, code, details? }. error is a short message for a human. code is stable and safe to match on in your own code. details carries extra fields for some codes, for example retryAfter on a 429.
| Code | Status | Description |
|---|---|---|
| VALIDATION_ERROR | 400 | The body failed validation: a wrong type, a key over the limit, a bad timestamp, or a timestamp more than one hour from now. |
| UNAUTHORIZED | 401 | The Authorization header is missing, or the token does not match the device or stream in the path. |
| INGESTION_BLOCKED | 403 | The organization that owns the device or stream has ingestion switched off. |
| NOT_FOUND | 404 | No route matches the path. |
| PAYLOAD_TOO_LARGE | 413 | The declared Content-Length is above the request body cap. |
| RATE_LIMIT_EXCEEDED | 429 | The device, stream or client IP sent more requests than its window allows. Wait retryAfter seconds. |
| INTERNAL_ERROR | 500 | Something failed inside Logable. The body carries no detail. Retry with backoff. |
Over MQTT#
There is no reply channel. A rejected message is dropped. The device log shows the reason.
Device log codes#
The device log on a device's Connection tab, and on the same device in the mobile app, shows what Logable did with each message and why. Each row carries one of these codes. Rejections and denials also leave a one-line notice on the device overview until the device sends an accepted message.
| Code | Kind | Description |
|---|---|---|
| invalid_topic | rejected | Published to an unknown topic. Publish device data to devices/{deviceId}/data. |
| parse_failed | rejected | The payload is not valid JSON, or has no data object. Send a JSON object with a data object of metric values. |
| validation_failed | rejected | The payload failed validation. Check the metric names and value types for this device type. |
| payload_too_large | rejected | The payload is larger than 1 MB. Send fewer metrics per message. |
| rate_limited | rejected | The device sent more than 10 messages per second. Send at most 10 messages per second from one device. |
| org_rate_limited | rejected | The organisation sent more than 200 messages per second. Reduce the total send rate across all devices. |
| ingestion_blocked | rejected | Ingestion is switched off for this organisation. Contact the organisation owner. |
| internal_error | rejected | The message could not be processed. Try again. If it repeats, contact support. |
| token_rejected | auth_denied | Authentication failed. The token does not match this device. Use the device id as the username and a current device token as the password. |
| client_id_reserved | auth_denied | Authentication failed. The client id belongs to another device. Use your own client id, or logable-{deviceId}. |
| missing_credentials | auth_denied | Authentication failed. No username or password was sent. Use the device id as the username and a current device token as the password. |
| topic_not_allowed | publish_denied | Publish denied. The topic is outside this device's namespace. Publish to devices/{deviceId}/data. |
| client_connected | connected | Connected. |
| client_disconnected | disconnected | Disconnected: the broker's reason (for example keepalive timeout). |
The kind column says what the row is about. rejected is a message Logable refused. auth_denied is a connection or request Logable refused. publish_denied is a topic outside the device namespace. connected and disconnected are what the MQTT broker saw.
Examples#
A validation error:
{
"error": "Invalid data payload",
"code": "VALIDATION_ERROR",
"details": {
"message": "exceeds max length"
}
}A rate limit:
{
"error": "Too many requests",
"code": "RATE_LIMIT_EXCEEDED",
"details": {
"message": "Rate limit: 10 requests per 1000ms per device",
"retryAfter": 1
}
}