Skip to main content

API Errors

SDD Classification: L3-Technical Authority: Engineering Team Review Cycle: Quarterly
This guide covers the error response format, HTTP status codes, error codes, and best practices for handling API errors in your applications.

Error Response Format

All API errors return a consistent JSON structure:
{
  "error": {
    "code": "DOCUMENT_NOT_FOUND",
    "message": "The requested document could not be found",
    "details": {
      "document_id": "123e4567-e89b-12d3-a456-426614174000",
      "workspace_id": "workspace_abc123"
    },
    "request_id": "req_1234567890abcdef",
    "timestamp": "2025-01-07T10:30:00Z"
  }
}

Error Object Properties

PropertyTypeDescription
codestringMachine-readable error code
messagestringHuman-readable error description
detailsobjectAdditional context (optional)
request_idstringUnique request identifier for support
timestampstringISO 8601 timestamp

HTTP Status Codes

Success Codes (2xx)

CodeStatusDescription
200OKRequest succeeded
201CreatedResource created successfully
204No ContentRequest succeeded, no response body

Client Error Codes (4xx)

CodeStatusDescriptionAction
400Bad RequestInvalid request formatCheck request body/parameters
401UnauthorizedMissing/invalid authenticationRefresh token or re-authenticate
403ForbiddenInsufficient permissionsCheck user permissions
404Not FoundResource doesn’t existVerify resource ID
409ConflictResource conflictRetry with fresh data
422Unprocessable EntityValidation failedCheck field values
429Too Many RequestsRate limit exceededWait and retry

Server Error Codes (5xx)

CodeStatusDescriptionAction
500Internal Server ErrorUnexpected server errorRetry with exponential backoff
502Bad GatewayUpstream service errorRetry after delay
503Service UnavailableService temporarily unavailableRetry after Retry-After header
504Gateway TimeoutRequest timed outRetry with shorter timeout

Error Code Reference

Authentication Errors

CodeHTTPDescriptionResolution
INVALID_CREDENTIALS401Email or password incorrectVerify credentials
INVALID_TOKEN401JWT token malformed or expiredRefresh token
TOKEN_EXPIRED401Access token has expiredUse refresh token
TOKEN_REVOKED401Token has been invalidatedRe-authenticate
REFRESH_TOKEN_EXPIRED401Refresh token has expiredRe-authenticate
INSUFFICIENT_PERMISSIONS403User lacks required permissionContact admin
ACCOUNT_LOCKED403Too many failed login attemptsWait or reset password
ACCOUNT_SUSPENDED403Account disabled by adminContact support
INSUFFICIENT_SCOPE403Token lacks required OAuth scopeRequest additional scopes

Resource Errors

CodeHTTPDescriptionResolution
DOCUMENT_NOT_FOUND404Document doesn’t existVerify document ID
WORKSPACE_NOT_FOUND404Workspace doesn’t existVerify workspace ID
USER_NOT_FOUND404User doesn’t existVerify user ID
VERSION_NOT_FOUND404Document version doesn’t existList available versions
TEMPLATE_NOT_FOUND404Template doesn’t existVerify template ID

Validation Errors

CodeHTTPDescriptionResolution
INVALID_REQUEST_BODY400Request body is malformedCheck JSON syntax
MISSING_REQUIRED_FIELD400Required field not providedInclude all required fields
INVALID_FIELD_VALUE422Field value is invalidCheck field constraints
TITLE_TOO_LONG422Title exceeds 500 charactersShorten title
CONTENT_TOO_LARGE422Content exceeds 10MB limitReduce content size
INVALID_EMAIL_FORMAT422Email format incorrectProvide valid email
INVALID_UUID_FORMAT422UUID format incorrectProvide valid UUID

Conflict Errors

CodeHTTPDescriptionResolution
VERSION_CONFLICT409Document was modifiedFetch latest, retry
DOCUMENT_LOCKED409Document being editedWait and retry
DUPLICATE_RESOURCE409Resource already existsUse unique identifier
CONCURRENT_MODIFICATION409Concurrent edit detectedMerge changes manually

Rate Limit Errors

CodeHTTPDescriptionResolution
RATE_LIMIT_EXCEEDED429Too many requestsWait for Retry-After
AI_QUOTA_EXCEEDED429Daily AI limit reachedUpgrade plan or wait
CONCURRENT_LIMIT429Too many concurrent connectionsClose unused connections

Server Errors

CodeHTTPDescriptionResolution
INTERNAL_ERROR500Unexpected server errorRetry, report if persistent
SERVICE_UNAVAILABLE503Service temporarily downRetry after delay
AI_SERVICE_ERROR503AI provider unavailableRetry or use fallback
DATABASE_ERROR503Database connection issueRetry after delay

Error Handling Best Practices

JavaScript/TypeScript Example

class MateriAPIError extends Error {
  constructor(
    public status: number,
    public code: string,
    public message: string,
    public details?: Record<string, unknown>,
    public requestId?: string
  ) {
    super(message);
    this.name = 'MateriAPIError';
  }
}

async function handleResponse(response: Response) {
  if (response.ok) {
    return response.json();
  }

  const error = await response.json();
  throw new MateriAPIError(
    response.status,
    error.error.code,
    error.error.message,
    error.error.details,
    error.error.request_id
  );
}

async function apiCall(endpoint: string, options?: RequestInit) {
  try {
    const response = await fetch(`https://api.materi.dev/v1${endpoint}`, {
      ...options,
      headers: {
        'Authorization': `Bearer ${accessToken}`,
        'Content-Type': 'application/json',
        ...options?.headers,
      },
    });
    return handleResponse(response);
  } catch (error) {
    if (error instanceof MateriAPIError) {
      switch (error.code) {
        case 'TOKEN_EXPIRED':
          await refreshToken();
          return apiCall(endpoint, options);

        case 'RATE_LIMIT_EXCEEDED':
          const retryAfter = 60; // seconds
          await delay(retryAfter * 1000);
          return apiCall(endpoint, options);

        case 'VERSION_CONFLICT':
          // Handle conflict resolution
          throw error;

        default:
          throw error;
      }
    }
    throw error;
  }
}

Retry Strategy with Exponential Backoff

async function retryWithBackoff(fn, maxRetries = 3) {
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    try {
      return await fn();
    } catch (error) {
      const isRetryable = [500, 502, 503, 504, 429].includes(error.status);

      if (!isRetryable || attempt === maxRetries) {
        throw error;
      }

      const delay = Math.pow(2, attempt) * 1000 + Math.random() * 1000;
      console.log(`Retry ${attempt + 1}/${maxRetries} after ${delay}ms`);
      await new Promise(resolve => setTimeout(resolve, delay));
    }
  }
}

Validation Error Details

Validation errors include detailed field-level information:
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Request validation failed",
    "details": {
      "fields": [
        {
          "field": "title",
          "code": "TITLE_TOO_LONG",
          "message": "Title must be 500 characters or less",
          "value_provided": "Very long title...",
          "max_length": 500
        },
        {
          "field": "email",
          "code": "INVALID_EMAIL_FORMAT",
          "message": "Email format is invalid",
          "value_provided": "not-an-email"
        }
      ]
    },
    "request_id": "req_abc123"
  }
}

Debugging with Request IDs

Every API response includes a unique request_id. When contacting support:
  1. Note the request_id from the error response
  2. Include the timestamp of the request
  3. Describe the action being performed
  4. Provide any relevant context
Example support request:
Request ID: req_1234567890abcdef
Timestamp: 2025-01-07T10:30:00Z
Action: Creating document in workspace ws_abc123
Error: INSUFFICIENT_PERMISSIONS


Document Status: Complete Version: 2.0