Error reference
Complete catalog of error codes, HTTP status codes, and error response format for the EmuView API.
#Error reference
Every EmuView API error returns a consistent JSON envelope. This page documents the response format and every error code the API can return.
#Error response format
{
"error": "error_code",
"message": "Human-readable description of what went wrong.",
"details": {},
"requestId": "8f1c0deadbeef123-SYD"
}
| Field | Type | Description |
|---|---|---|
error |
string |
Machine-readable error code (e.g., not_found, forbidden) |
message |
string |
Human-readable message with context about the failure |
details |
object|null |
Structured details for validation errors (field-level error messages) |
requestId |
string |
The id the server logged this request under |
#requestId
Every error body carries the id the gateway logged that request under —
Cloudflare's cf-ray where there is one, a generated ULID off-platform. The
same string appears in the server's [gateway:error] line, so quoting it in a
bug report is what turns "the API returned a 500 yesterday" into one log line.
It is on every error status, not only 5xx: a 403 or a 404 that turns out to
be a bug is exactly the report that needs tracing. It is not a secret —
Cloudflare already returns cf-ray as a response header on every request it
serves.
#Validation error details
When the error code is validation_error, the details field contains per-field error information:
{
"error": "validation_error",
"message": "One or more fields failed validation.",
"details": {
"fields": {
"title": "Title is required.",
"price": "Price must be greater than 0.",
"email": "Invalid email format."
}
}
}
#SDK error handling
The SDK throws ApiError objects with typed properties:
import { ApiError } from '@emuview/sdk';
try {
await sdk.collection('products').create({ title: '' });
} catch (err) {
if (err instanceof ApiError) {
console.error(err.code); // "validation_error"
console.error(err.message); // "One or more fields failed validation."
console.error(err.status); // 422
console.error(err.details); // { fields: { title: "Title is required." } }
}
}
#HTTP status codes
| Status | Meaning |
|---|---|
200 |
OK — successful read or update |
201 |
Created — successful resource creation |
202 |
Accepted — async operation started (automation flows) |
204 |
No Content — successful deletion with no body |
400 |
Bad Request — invalid input, missing fields, or limit exceeded |
401 |
Unauthorized — missing or invalid authentication |
403 |
Forbidden — insufficient permissions |
404 |
Not Found — resource does not exist |
409 |
Conflict — resource already exists or concurrent edit |
412 |
Precondition Failed — optimistic concurrency check failed |
422 |
Unprocessable Entity — field validation errors |
429 |
Too Many Requests — rate limited |
500 |
Internal Server Error |
503 |
Service Unavailable |
#Error code catalog
#Authentication errors (401)
| Code | Message |
|---|---|
unauthorized |
A valid Bearer token or session cookie is required. |
#Authorization errors (403)
| Code | Message |
|---|---|
forbidden |
Insufficient permissions for this action. |
entitlement_required |
This capability is not included in your plan. |
mfa_required |
This role requires Multi-Factor Authentication. |
account_banned |
This account has been banned. |
install_needs_person |
A blueprint install needs a person; this API key has no bound user. |
crew_needs_person |
Creating a crew needs a person — its creator becomes its first admin member. |
upload_needs_person |
An upload needs a person — a file records its uploader as its owner. |
signup_forbidden |
Registration is not permitted. |
validation_rule_failed |
The data violates a permission validation rule. |
blocked_by_automation |
Blocked by automation. |
The three
*_needs_personcodes are not permission errors, and the distinction is why they are separate codes: the caller is an API key with no bound user, so the fix is a different credential — the dashboard, or a delegated key — never a broader grant. They are returned before the permission check's ownforbiddenwould be, so the two never arrive together. Every other write an API key makes is unaffected; these three endpoints record their caller as an owner, and an ownerless row would be unusable rather than merely unattributed.
#Validation errors (400)
| Code | Message |
|---|---|
invalid_json |
Request body must be valid JSON. |
invalid_request |
Invalid request parameters. |
invalid_name |
Name contains invalid characters or is too short. |
invalid_field |
One or more fields are invalid. |
reserved_name |
This name is reserved by the system. |
reserved_field |
This field name is reserved by the system. |
missing_field |
A required field is missing. |
limit_exceeded |
Request exceeds allowed limits. |
duplicate_field |
A field with this name already exists. |
invalid_input |
Invalid input provided. |
invalid_mode |
Invalid mode specified. |
invalid_column |
Invalid column name. |
#Validation errors (422)
| Code | Message |
|---|---|
validation_error |
One or more fields failed validation. |
#Resource errors (404)
| Code | Message |
|---|---|
not_found |
Resource not found. |
collection_not_found |
Collection not found. |
record_not_found |
Record not found. |
database_not_found |
Database not found. |
user_not_found |
User not found. |
#Conflict errors (409)
| Code | Message |
|---|---|
already_exists |
A resource with this identifier already exists. |
conflict |
Conflicting update detected. |
#Precondition errors (412)
| Code | Message |
|---|---|
precondition_failed |
Record has been modified by another process. |
#Rate limiting (429)
| Code | Message |
|---|---|
rate_limited |
Too many requests. Please retry after the indicated time. |
#Server errors (500/503)
| Code | Message |
|---|---|
internal_server_error |
An internal error occurred. |
ddl_error |
Database schema operation failed. |
auth_error |
Auth handler error. |
service_unavailable |
Service temporarily unavailable. |
kv_unavailable |
KV namespace not configured. |
#Rate limiting
Auth endpoints (/api/auth/*) are rate-limited to 5 requests per minute per IP address. When rate limited, the response includes a Retry-After header indicating how many seconds to wait.
{
"error": "rate_limited",
"message": "Too many requests. Please retry after the indicated time."
}
#Failure modes
| Endpoint type | On rate limiter unavailability | Rationale |
|---|---|---|
/api/auth/* |
Fail closed — request rejected with 429 |
Brute-force attacks must not bypass rate limits |
| All other endpoints | Fail open — request allowed through | Availability is preferred over perfect rate limiting |
#Common error scenarios
#Missing authentication
Request: Any authenticated endpoint without an Authorization header.
{
"error": "unauthorized",
"message": "A valid Bearer token or session cookie is required."
}
Fix: Include Authorization: Bearer <token> in the request header.
#Permission denied
Request: An endpoint the user's role does not have access to.
{
"error": "forbidden",
"message": "Insufficient permissions for this action."
}
Fix: Check the user's role and the collection's access control policy. Admins can update policies in Settings → Roles & Permissions.
#Record version conflict
Request: An update with a stale version token.
{
"error": "precondition_failed",
"message": "Record has been modified by another process."
}
Fix: Fetch the latest record, re-apply your changes, and retry with the updated updated_at value.