> ## Documentation Index
> Fetch the complete documentation index at: https://docs.usehasp.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Error Handling

> Four error types, ErrorCode constants, validation field errors, and the complete error handling pattern.

The SDK throws four error types. Always check for `AbortError` first (request cancellation), then `NetworkError`, then `MaintenanceError`, then the base `HaspSDKError`.

## Error Types

### DOMException (AbortError)

Thrown when a request is cancelled via `AbortSignal` or `sdk.destroy()`. This is not a network or server error.

```javascript theme={null}
if (error instanceof DOMException && error.name === 'AbortError') {
  return; // cancelled — no action needed
}
```

### NetworkError

Thrown when the `fetch()` call itself fails — device offline, DNS failure, etc. GET requests are retried automatically before this fires.

```javascript theme={null}
if (error instanceof HaspSDK.NetworkError) {
  showMessage('Check your internet connection.');
}
```

### MaintenanceError

Thrown proactively when write operations are attempted while the SDK is in maintenance state.

```javascript theme={null}
if (error instanceof HaspSDK.MaintenanceError) {
  showMessage('App is being updated. Try again shortly.');
}
```

### HaspSDKError

The base class for all server-side errors. Check `error.code` against `ErrorCode` constants.

```javascript theme={null}
if (error instanceof HaspSDK.HaspSDKError) {
  const { ErrorCode } = HaspSDK;

  if (error.code === ErrorCode.ValidationFailed) {
    const fieldErrors = error.getFieldErrors();
    showFieldErrors(fieldErrors);
  } else if (error.code === ErrorCode.NotFound) {
    showMessage('Record not found.');
  } else if (error.code === ErrorCode.AccessDenied) {
    showMessage('You do not have permission.');
  } else if (error.code === ErrorCode.RateLimited) {
    showMessage('Too many requests. Try again shortly.');
  } else {
    showMessage('Something went wrong. Please try again.');
  }
}
```

## Complete Pattern

```javascript theme={null}
const { ErrorCode, NetworkError, MaintenanceError, HaspSDKError } = HaspSDK;

try {
  await sdk.createRecord('tasks', data);
} catch (error) {
  if (error instanceof DOMException && error.name === 'AbortError') {
    return;
  }
  if (error instanceof NetworkError) {
    showMessage('Check your internet connection.');
  } else if (error instanceof MaintenanceError) {
    showMessage('App is being updated. Try again shortly.');
  } else if (error instanceof HaspSDKError) {
    if (error.code === ErrorCode.ValidationFailed) {
      showFieldErrors(error.getFieldErrors());
    } else {
      showMessage(error.message);
    }
  }
}
```

## ErrorCode Constants

| Constant                        | Value                   | When thrown                                                                |
| ------------------------------- | ----------------------- | -------------------------------------------------------------------------- |
| `ErrorCode.Unauthorized`        | `UNAUTHORIZED`          | Session expired                                                            |
| `ErrorCode.AccessDenied`        | `ACCESS_DENIED`         | No permission for this app or record                                       |
| `ErrorCode.MissingAppId`        | `MISSING_APP_ID`        | Constructor called without appId and `window.__HASP__` not set             |
| `ErrorCode.NotFound`            | `NOT_FOUND`             | Record, entity, or app not found                                           |
| `ErrorCode.ReadOnlyRecord`      | `READ_ONLY_RECORD`      | Record is read-only                                                        |
| `ErrorCode.ValidationFailed`    | `VALIDATION_FAILED`     | Field validation error — check `getFieldErrors()`                          |
| `ErrorCode.StorageLimitReached` | `STORAGE_LIMIT_REACHED` | Storage quota exceeded                                                     |
| `ErrorCode.RateLimited`         | `RATE_LIMITED`          | Too many requests — retried automatically; throws if all retries exhausted |
| `ErrorCode.QueryTooComplex`     | `QUERY_TOO_COMPLEX`     | Filter is too deeply nested or complex                                     |
| `ErrorCode.Maintenance`         | `MAINTENANCE`           | App is in maintenance mode                                                 |
| `ErrorCode.PreviewMode`         | `PREVIEW_MODE`          | Write attempted while the app is running in preview                        |
| `ErrorCode.NetworkError`        | `NETWORK_ERROR`         | Network unreachable                                                        |
| `ErrorCode.Unknown`             | `UNKNOWN`               | Unexpected server error                                                    |

## Validation Field Errors

When `error.code === ErrorCode.ValidationFailed`, call `error.getFieldErrors()` to get a `{ fieldKey: 'message' }` map of the first error per field:

```javascript theme={null}
const fieldErrors = error.getFieldErrors();
// { title: 'This field is required.', status: 'Invalid value.' }

Object.entries(fieldErrors).forEach(([field, message]) => {
  const el = document.getElementById(`error-${field}`);
  if (el) el.textContent = message;
});
```

## 401 Handling

On 401, the SDK calls `onUnauthorized()` (defaults to redirecting to `/login`) then throws `HaspSDKError` with `ErrorCode.Unauthorized`. Override `onUnauthorized` in the constructor to save draft state before redirecting.
