Common Errors
Every error returned by MemorySync uses a structured JSON envelope with a machine-readable error.code, a human-readable error.message, and the request_id for tracing. This page catalogs every error status code, explains exactly when it fires, and shows you how to fix it.
Error Response Format
All error responses share a consistent outer structure. The request_id is always present — it's the first thing to grab when debugging:
{
"error": {
"code": "VALIDATION_ERROR", // Machine-readable, always present
"message": "query: Field required", // Human-readable description
"details": [...] // Only on 422 responses
},
"request_id": "a1b2c3d4-..." // Always present, even on 500s
}
Some older error paths use a flat format: {"error": "ValidationError", "message": "...", "request_id": "..."}. Both formats include request_id. In your error-handling code, check for error.code first, then fall back to error as a string.
400 Bad Request
A 400 response means the request was syntactically valid but semantically wrong. The platform understood your request format but rejected its content.
| Error Code | When It Fires | Fix |
|---|---|---|
ValidationError | Business logic validation failed (e.g., invalid date range, conflicting parameters) | Read the message field — it describes the exact constraint that was violated |
INVALID_TENANT_ID_FORMAT | The X-Tenant-ID header contains characters that fail format validation (injection defense) | Use only alphanumeric characters and hyphens in tenant IDs |
TENANT_CONTEXT_REQUIRED | The authenticated user has no default tenant and the X-Tenant-ID header was not sent | Add X-Tenant-ID: your-tenant-id to the request headers |
💡 Note: The TENANT_CONTEXT_REQUIRED error is a security feature. Without an explicit tenant, the system cannot decide which tenant’s data to scope the request to, so the request is rejected rather than silently selecting the wrong tenant.
401 Unauthorized
A 401 means the system could not identify the caller. No valid credentials were found across any of the supported authentication methods.
| Message | Cause | Fix |
|---|---|---|
"API key missing" | No X-API-Key header present and no Bearer token provided | Add X-API-Key: ms_... to your request headers |
"Invalid API key" | The key is unknown to the system or no longer in an active state | Check the key in your dashboard → API Keys. Regenerate if needed. |
"Invalid consumer token" | The consumer token could not be validated against an active consumer account | Request a fresh consumer token via the consumer auth endpoint |
"Invalid or missing authentication credentials" | No supported authentication method on the request resolved a valid user | Verify your auth method — see the Authentication Issues page |
When the 401 is for API keys, the platform records a security audit event with a short key prefix and a failure reason. These entries are visible in the audit log under the api_key category and are useful for spotting repeated failed attempts.
403 Forbidden
A 403 means the system identified the caller but the caller lacks permission for the requested operation. Unlike 401, the credentials are valid — the authorization check failed.
| Message | Cause | Fix |
|---|---|---|
"API key revoked" | Key exists but has been revoked | Generate a new API key. Revoked keys cannot be reactivated. |
"User account is disabled" | The user's status is set to disabled | Contact your organization admin to re-enable the account |
"User account is suspended" | The user's status is set to suspended | Contact your organization admin |
"Admin access required" | The endpoint requires admin or owner role | Use an account or API key with admin role |
"Header/API key tenant mismatch" | The X-Tenant-ID header doesn't match the API key's tenant | Remove the X-Tenant-ID header — API key tenant is the source of truth |
"Header/API key project mismatch" | The X-Project-ID header doesn't match the API key's project | Remove the X-Project-ID header — API key project is the source of truth |
"Not authorized for this tenant" | User tried to switch to a tenant they don't have membership in | Verify the user belongs to the organization that owns the target tenant |
"Consumer account disabled" | Consumer user status is not "active" | Re-enable the consumer account via the management API |
422 Validation Error
A 422 means the request body failed schema validation. The response always includes a details array with per-field error information:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "messages → 0 → content: Field required",
"details": [
{
"loc": ["body", "messages", 0, "content"],
"msg": "Field required",
"type": "missing"
}
]
},
"request_id": "..."
}
Reading the loc path: The location array describes the path to the invalid field. ["body", "messages", 0, "content"] means "in the request body → the messages array → first element → the content field". The "body" prefix is always present and can be ignored.
Common validation errors:
"Field required"— a mandatory field is missing from your request body"Input should be a valid string"— wrong data type (e.g., sending a number where a string is expected)"Value error, ..."— a custom business rule failed (the message after the prefix describes the rule)
💡 Tip: When the error message starts with "Value error, ", the platform strips that prefix in the top-level message field so you see the actual constraint message directly.
429 Rate Limit Exceeded
A 429 means you've exceeded one of the three rate-limit layers (per-second, per-minute, or per-hour). The response tells you exactly which layer blocked you and when to retry:
{
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Rate limit exceeded (per-minute). Try again in 12 seconds.",
"limits": { "per_second": 10, "per_minute": 200, "per_hour": 5000 },
"blocked_by": "per_minute",
"retry_after": 12
}
}
// Response headers:
// Retry-After: 12
// X-RateLimit-Limit: 200
// X-RateLimit-Remaining: 0
// X-RateLimit-Reset: 1715265600
How to handle in code:
- Read the
Retry-Afterresponse header — this is the number of seconds to wait - Check
error.blocked_byto understand which layer is the bottleneck - If
blocked_byis"per_second", add a small delay between requests (you're bursting too fast) - If
blocked_byis"per_hour", you're hitting your budget limit — consider upgrading your plan tier
For a deep dive into rate limit tiers and response headers, see the Rate Limit Issues page.
500 Internal Server Error
A 500 is always a platform-side issue — never your fault as a caller. The response is intentionally minimal to avoid leaking internal details:
{
"error": "InternalServerError",
"message": "An unexpected error occurred",
"request_id": "a1b2c3d4-e5f6-..."
}
What happens server-side: The full failure detail is recorded internally for post-mortem analysis. The request_id in the response correlates your error to that server-side record so support can investigate the exact failure.
What to do:
- Retry once — transient infrastructure issues can cause sporadic 500s
- Check
GET /health— if a component is"unhealthy", wait for it to recover - Contact support — include the
request_id, the endpoint, the method, and the approximate timestamp
⚠️ Important: If you're getting a 500 consistently on the same request payload, it's likely a reproducible bug. Include the full request body (with sensitive values redacted) when contacting support so the issue can be reproduced.