Docs/Guides/Errors

Errors

Every error response is JSON. The shape depends on the status code.

Standard error

For 400, 401, 403, 404, 409, 429, and 5xx:

JSON
{
   "message": "Invalid credentials"
}

That's the whole body. The HTTP status code is the source of truth; the message is human-readable text intended for logs and debugging — never display it directly to end users without translation.

Validation errors

For 422 Unprocessable Entity, the body includes a violations array:

JSON
{
   "message": "Validation failed",
   "violations": [
      {
         "propertyPath": "email",
         "message": "Invalid email format"
      },
      {
         "propertyPath": "password",
         "message": "Password must be at least 8 characters"
      }
   ]
}

The propertyPath matches the request DTO field name and can be used to highlight the offending input.

Status codes

CodeMeaningWhen to retry
200OK
201Created
204No Content (e.g. DELETE success)
302Redirect (OAuth callbacks, customs)
400Bad request — missing or malformed parametersNever (fix the request)
401Unauthorized — missing, invalid, or expired tokenAfter refreshing the token
403Forbidden — token valid but action not allowedNever
404Not foundNever
409Conflict — resource exists or constraint violatedNever
422Validation failedNever (fix the input)
429Rate limitedAfter Retry-After seconds
5xxServer errorWith exponential backoff

Rate limit headers

429 responses include Retry-After (in seconds). The SDK respects this header automatically.

Error handling with the SDK

The SDK throws typed exceptions for each category:

TypeScript
import {
   AuthenticationError,
   AuthorizationError,
   ValidationError,
   ConflictError,
   NotFoundError,
   RateLimitError,
   NetworkError
} from '@katforge/api';

try {
   await katforge.users.create ({ email, username, password });
} catch (err) {
   if (err instanceof ValidationError) {
      // err.violations is the parsed array
      err.violations.forEach (v => showFieldError (v.propertyPath, v.message));
   } else if (err instanceof ConflictError) {
      showError ('Username or email already taken');
   } else if (err instanceof RateLimitError) {
      showError (`Too many attempts. Try again in ${err.retryAfter}s.`);
   } else if (err instanceof NetworkError) {
      showError ('Could not reach the server. Check your connection.');
   } else {
      throw err;
   }
}

All error classes extend KATforgeError, which extends the built-in Error.

Token refresh

AuthenticationError (i.e. 401) is special. The SDK intercepts it, attempts to refresh the access token, and replays the original request once. You only see this exception if the refresh itself fails — at which point the user has to log in again.

You should treat any AuthenticationError that escapes the SDK as "session over" and trigger a re-login flow.