Errors
Errors use conventional HTTP status codes and a single JSON body shape.
{
"code": "insufficient_scope",
"message": "This API key does not hold the required scope: operations.write",
"statusCode": 403
}
Every response carries x-request-id. Log it — it identifies your exact request
in MineTech's logs and is the first thing support will ask for.
Status codes
| Status | Meaning | Retry? |
|---|---|---|
400 | Malformed request | No — fix the request |
401 | Authentication failed | No — see Authentication |
403 | Authenticated but not permitted | No — grant the scope |
404 | No such resource, or it belongs to another tenant | No |
409 | Conflicting state | Depends — read the message |
422 | Validation failed | No — see fieldErrors |
429 | Rate limit exceeded | Yes — honour Retry-After |
5xx | Server-side failure | Yes — with backoff |
A 404 on a resource you believe exists usually means it belongs to a different
tenant. Tenant scoping is applied before existence checks, deliberately: a 403
would confirm the record exists to someone not entitled to know that.
Validation errors
{
"code": "validation_failed",
"statusCode": 422,
"message": "title should not be empty; severity must be a valid enum value",
"fieldErrors": {
"title": ["should not be empty"],
"severity": ["must be a valid enum value"]
}
}
Handling errors with the SDK
Every failure is a typed subclass:
import {
ApiError, ValidationError, RateLimitError,
PermissionError, TimeoutError, ConnectionError,
} from '@minetech/node/errors';
try {
await client.safety.incidents.create(payload);
} catch (error) {
if (error instanceof ValidationError) {
return showFieldErrors(error.fieldErrors);
}
if (error instanceof PermissionError) {
return alertOperator(`Key is missing a scope: ${error.message}`);
}
if (error instanceof RateLimitError) {
return scheduleRetry(error.retryAfterSeconds);
}
if (error instanceof TimeoutError || error instanceof ConnectionError) {
// Already retried internally; the network is genuinely unavailable.
return markDegraded();
}
if (error instanceof ApiError) {
log.error({ status: error.status, code: error.code, requestId: error.requestId });
}
throw error;
}
The SDK retries 408, 429 and 5xx automatically with exponential backoff and
jitter, honouring Retry-After. By the time an error reaches you, retrying has
already been attempted and failed.
4xx responses are never retried — they would fail identically, and retrying only
delays the error you need to see.