Reference
Error codes
Every Lumin error follows the same shape: an MCP error object with a stable code, a human-readable reason, and (where useful) a recovery hint.
The error shape
json
{
"isError": true,
"content": [{
"type": "text",
"text": "This tool needs birth data. Call set_birth_profile first."
}],
"_meta": {
"code": "birth_data_missing",
"recoverable": true
}
}The one exception is the rate limit. An exhausted monthly allowance does not arrive as a tool error. It arrives as HTTP 429 with a JSON-RPC error object, code -32000, carrying retryAfterSeconds, limit, remaining, and resetAt in error.data. See rate limits for the full shape.
All codes
| Code | HTTP | Cause | Recovery |
|---|---|---|---|
| -32000 | 429 | Monthly call allowance used up. A JSON-RPC error object on the response, not a tool error | Read error.data.resetAt, tell the user or buy a call pack, do not sleep-retry |
| invalid_event_name | 400 | Event keyword not recognized | Map to nearest known event or pass house_group override |
| birth_data_missing | 400 | Tool requires birth data and none in context | Call set_birth_profile, then retry once |
| validation_failed | 400 | Schema validation rejected input | Fix input, do not retry blindly |
| auth_invalid | 401 | Bearer token rejected | Refresh OAuth or rotate API key |
| auth_expired | 401 | OAuth token expired | Refresh against /token endpoint |
| user_suspended | 403 | Account is suspended | Contact support, no automated recovery |
| engine_unavailable | 502 | kp-engine transient failure | Exponential backoff, max 2 retries |
| engine_timeout | 504 | kp-engine slow response | Retry with timeout extension or skip phase |
| internal_error | 500 | Unhandled MCP-side error | Retry once, then escalate with request id |
Idempotency
All Lumin tool calls are pure. Retrying produces the same result, and failed calls do not count against your monthly allowance.