# Error handling

The SDK provides typed error classes and utilities so you can handle failures precisely — whether it's an expired token, a blocked popup, or a failed verification.

## Error Types

All SDK errors are instances of `HumanityReactError`, which carries:

* **`message`** — human-readable description
* **`code`** — machine-readable error code (e.g. `'invalid_grant'`, `'token_expired'`, `'popup_blocked'`)
* **`status`** — HTTP status code when applicable

```tsx
import { isHumanityError, type HumanityReactError } from '@humanity-org/react-sdk';

function handleError(err: unknown) {
  if (isHumanityError(err)) {
    console.error('Humanity error:', {
      message: err.message,
      code: err.code,
      status: err.status,
    });
  }
}
```

## Global Error Handler

Use the `onError` prop on `HumanityProvider` to catch all auth and verification errors in one place — ideal for routing to an error reporting service:

```tsx
import { HumanityProvider } from '@humanity-org/react-sdk';
import * as Sentry from '@sentry/react';

<HumanityProvider
  clientId="hp_xxx"
  redirectUri="https://app.com/callback"
  onError={(error) => {
    Sentry.captureException(error, {
      extra: { code: error.code, status: error.status },
    });
  }}
>
  <App />
</HumanityProvider>
```

## Per-Component Error Callbacks

Each component that performs async operations (`HumanityConnect`, `HumanityVerify`) accepts an `onError` prop for local handling:

```tsx
<HumanityConnect
  scopes={['openid', 'profile']}
  onSuccess={(result) => handleLogin(result)}
  onError={(error) => {
    if (error.code === 'popup_blocked') {
      showToast('Please allow popups for this site.');
    } else {
      showToast('Login failed. Please try again.');
    }
  }}
/>
```

## Error Boundary

Wrap authentication-critical UI with `HumanityErrorBoundary` to catch unexpected render-time errors:

```tsx
import { HumanityErrorBoundary, HumanityConnect } from '@humanity-org/react-sdk';

<HumanityErrorBoundary
  fallback={(error) => (
    <div>
      <p>Authentication unavailable: {error.message}</p>
      <button onClick={() => window.location.reload()}>Retry</button>
    </div>
  )}
  onError={(error, info) => {
    console.error('Boundary caught:', error, info.componentStack);
  }}
>
  <HumanityConnect />
</HumanityErrorBoundary>
```

## Hook-Level Error State

Hooks expose an `error` field you can check directly:

```tsx
import { useAuth, useVerification } from '@humanity-org/react-sdk';

function AuthStatus() {
  const { error: authError } = useAuth();
  const { status, error: verifyError } = useVerification();

  if (authError) return <p>Auth error: {authError.message}</p>;
  if (status === 'error') return <p>Verification error: {verifyError?.message}</p>;

  return null;
}
```

## Error Utilities

| Export            | Description                                                         |
| ----------------- | ------------------------------------------------------------------- |
| `isHumanityError` | Type guard — returns `true` if the value is a `HumanityReactError`. |
| `createError`     | Factory for creating typed SDK errors.                              |
| `normalizeError`  | Converts any thrown value into a `HumanityReactError`.              |
| `parseOAuthError` | Parses OAuth error parameters from a callback URL.                  |

## Common Error Codes

| Code             | Cause                                               | Fix                                                   |
| ---------------- | --------------------------------------------------- | ----------------------------------------------------- |
| `popup_blocked`  | Browser blocked the OAuth popup                     | Prompt user to allow popups, or use `mode="redirect"` |
| `token_expired`  | Access token has expired                            | Call `refreshToken()` or prompt re-login              |
| `invalid_grant`  | Refresh token is invalid or revoked                 | Prompt re-login                                       |
| `access_denied`  | User declined the OAuth consent screen              | Show an informative message and retry option          |
| `network_error`  | Request failed due to connectivity                  | Retry after checking network                          |
| `scope_mismatch` | Requested scopes don't match registered application | Verify scopes in the Developer Portal                 |

## Production Checklist

Before going live, verify:

* [ ] `onError` global handler is configured and routes to your monitoring service
* [ ] `HumanityErrorBoundary` wraps authentication-critical UI
* [ ] `popup_blocked` case is handled (show instructions or fall back to redirect mode)
* [ ] Token expiry is handled gracefully (auto-refresh or re-login prompt)
* [ ] Redirect URI is registered for both sandbox and production environments
* [ ] `storage="memory"` is used, or you've explicitly accepted the XSS risk of `localStorage`
