Skip to main content

Error Reference

All SpatialFlow API errors follow a consistent JSON format. This page documents every error code, HTTP status pattern, and common error scenarios.

Error Response Format

Every error response includes a detail field with a human-readable message:

{
"detail": "Human-readable error message",
"error_code": "MACHINE_READABLE_CODE",
"details": {}
}
FieldTypeRequiredDescription
detailstring or arrayYesHuman-readable message (string) or validation errors (array)
error_codestringNoMachine-readable identifier for programmatic handling
detailsobjectNoAdditional context (field errors, limits, etc.)

Validation Errors

When request body validation fails (HTTP 422), the detail field is an array of error objects instead of a string:

{
"detail": [
{
"type": "missing",
"loc": ["body", "name"],
"msg": "Field required"
},
{
"type": "value_error",
"loc": ["body", "geometry", "type"],
"msg": "Value error, unsupported geometry type"
}
]
}

Each validation error object contains:

FieldDescription
typeError type (e.g., missing, value_error, string_too_long)
locLocation path as an array (e.g., ["body", "name"])
msgHuman-readable error message

HTTP Status Codes

StatusMeaningWhen It Occurs
400Bad RequestInvalid input, malformed JSON, business rule violation
401UnauthorizedMissing or invalid authentication token or API key
403ForbiddenValid auth but insufficient permissions, or quota exceeded (150%)
404Not FoundResource does not exist or is not accessible to the caller
422Unprocessable EntityRequest body fails schema validation
429Too Many RequestsRate limit or quota throttle exceeded
500Internal Server ErrorUnexpected server failure

Error Codes Reference

The error_code field provides a machine-readable identifier for programmatic error handling. Not all errors include this field.

Error CodeHTTP StatusDescription
RATE_LIMIT_EXCEEDED429Global rate limit exceeded (requests per hour)
RATE_LIMITED429Event quota soft throttle (usage at 120%)
QUOTA_EXCEEDED403Event quota hard cap (usage at 150%)
INSUFFICIENT_PERMISSIONS403API key missing required permission scope
NOT_FOUND404Requested resource does not exist
VALIDATION_ERROR422Request body failed schema validation
INVALID_GEOMETRY400Geometry validation failed (invalid type, too many vertices, self-intersection)
DUPLICATE_RESOURCE400Resource with the same unique identifier already exists
Always Check the detail Field First

Not all errors include an error_code. The detail field is always present and provides a human-readable description. Use error_code for programmatic branching when available, and fall back to detail for display.

Common Error Scenarios

Authentication Failure (401)

curl -i https://api.spatialflow.io/api/v1/geofences \
-H "Authorization: Bearer expired-or-invalid-token"
{
"detail": "Invalid or expired token"
}

Solution: Refresh your JWT token or verify your API key is correct and not revoked.

Rate Limit Exceeded (429)

curl -i https://api.spatialflow.io/api/v1/geofences \
-H "Authorization: Bearer YOUR_API_KEY"
HTTP/1.1 429 Too Many Requests
Retry-After: 3600
Content-Type: application/json
{
"detail": "Rate limit exceeded",
"error_code": "RATE_LIMIT_EXCEEDED",
"details": {
"limit": 1000,
"period_seconds": 3600
}
}

Solution: Wait for the number of seconds specified in the Retry-After header before retrying. See Rate Limiting for retry patterns.

Validation Error (422)

curl -i -X POST https://api.spatialflow.io/api/v1/geofences \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"geometry": {"type": "InvalidType", "coordinates": []}}'
{
"detail": [
{
"type": "missing",
"loc": ["body", "name"],
"msg": "Field required"
},
{
"type": "value_error",
"loc": ["body", "geometry", "type"],
"msg": "Value error, unsupported geometry type"
}
]
}

Solution: Check the loc field in each error to identify which request fields need correction.

Handling Errors in Code

Python

import requests

response = requests.post(
"https://api.spatialflow.io/api/v1/geofences",
headers={"Authorization": "Bearer YOUR_API_KEY"},
json={"name": "Test"},
)

if not response.ok:
error = response.json()
detail = error.get("detail")

# Validation errors return detail as an array
if isinstance(detail, list):
for err in detail:
print(f" {'.'.join(err['loc'])}: {err['msg']}")
else:
error_code = error.get("error_code")
if error_code == "RATE_LIMIT_EXCEEDED":
retry_after = int(response.headers.get("Retry-After", 60))
print(f"Rate limited. Retry in {retry_after}s")
else:
print(f"Error: {detail}")

TypeScript

async function handleApiError(response: Response): Promise<never> {
const body = await response.json();

if (Array.isArray(body.detail)) {
// Validation errors
const fields = body.detail.map(
(e: { loc: string[]; msg: string }) => `${e.loc.join(".")}: ${e.msg}`,
);
throw new Error(`Validation failed:\n${fields.join("\n")}`);
}

if (body.error_code === "RATE_LIMIT_EXCEEDED") {
const retryAfter = response.headers.get("Retry-After") ?? "60";
throw new Error(`Rate limited. Retry in ${retryAfter}s`);
}

throw new Error(body.detail ?? "Unknown error");
}

See Best Practices for retry patterns and production error handling strategies.