Error handling
@hyperyai/sdk error handling — HyperyModals for automatic re-auth and funds modals, RestrictionModal, SpendingLimitAlert, InsufficientCreditsAlert, ErrorBoundary, useError, parseError and the is*Error helpers.
Hypery returns failures as { "error": { "code", "message", "type", ... } }. The SDK
classifies them into a ParsedError and ships UI for the
ones a user can fix: signing in again, adding funds, or adding a card.
There are two ways to wire it:
- Automatic — make requests with
authenticatedFetchand mount<HyperyModals />once. Nothing else to wire. - Manual — catch errors yourself, keep them in
useError, and renderRestrictionModalor the inline alerts.
Error codes
When a body has no recognised code, parseError falls back to the HTTP status
(if the object carries status, statusCode or response.status): 402 →
INSUFFICIENT_CREDITS, 401 → UNAUTHENTICATED, 403 → PERMISSION_DENIED,
429 → RATE_LIMITED. Anything else becomes UNKNOWN_ERROR with the object's
message.
See Spending limits and Credits for when the billing errors occur.
HyperyModals
Mount once anywhere inside HyperyProvider. It reads the provider state that
authenticatedFetch sets:
- a 402/429 restriction → opens
RestrictionModal - a 401 that survives the silent refresh → opens
AuthModal
Closing a modal clears the state.
import { HyperyProvider, HyperyModals, useAuth } from '@hyperyai/sdk';
<HyperyProvider config={config}>
<App />
<HyperyModals branding={{ appName: 'Acme' }} onRetry={() => window.dispatchEvent(new Event('retry'))} />
</HyperyProvider>;
function App() {
const { authenticatedFetch, gatewayUrl } = useAuth();
// A 402 here opens the funds modal automatically.
const run = () => authenticatedFetch(`${gatewayUrl}/api/v1/chat/completions`, { method: 'POST', body: '…' });
return <button onClick={run}>Run</button>;
}All props are optional. If you pass onUnauthorized / onRestricted in the
provider config
the state is still set, so HyperyModals still opens.
RestrictionModal
The funds modal. When error is non-null it loads GET /api/wallet/state and shows
the action that fits:
After funds or a card are added it shows Continue, which calls onRetry and
onClose. Failures show the gateway's message (from { error: '…' },
{ error: { message } } or { message } bodies). Renders nothing when error is null.
import { RestrictionModal, useAuth, useError } from '@hyperyai/sdk';
function Chat() {
const { authenticatedFetch, getAccessToken, gatewayUrl, clientId } = useAuth();
const { error, setError, clearError, isBillingRestriction } = useError();
const send = async () => {
const res = await authenticatedFetch(`${gatewayUrl}/api/v1/chat/completions`, { method: 'POST', body: '…' });
if (!res.ok) setError({ ...(await res.json()), status: res.status });
};
return (
<>
<button onClick={send}>Send</button>
<RestrictionModal
error={isBillingRestriction && error ? { ...error.data, code: error.code, message: error.message } : null}
clientId={clientId}
gatewayUrl={gatewayUrl}
getAccessToken={getAccessToken}
onClose={clearError}
onRetry={send}
/>
</>
);
}RestrictionModal takes the raw error object, not a ParsedError. Pass the
response body's error directly, or spread parsed.data as above.
Inline alerts
SpendingLimitAlert
An inline alert for SPENDING_LIMIT_EXCEEDED: the message, limitType usage
(current / limit credits used) and the reset time. Renders nothing unless
error.isSpendingLimit.
import { SpendingLimitAlert, parseError } from '@hyperyai/sdk';
<SpendingLimitAlert error={parseError(body)} onRetry={retry} onUpgradeLimits={() => router.push('/billing')} />InsufficientCreditsAlert
An inline alert for INSUFFICIENT_CREDITS, showing available vs required.
Renders nothing unless error.isInsufficientCredits.
import { InsufficientCreditsAlert } from '@hyperyai/sdk';
<InsufficientCreditsAlert error={parsed} onAddCredits={() => checkout({ kind: 'topup', usdAmount: 10 })} />ErrorBoundary
A React error boundary that also displays API errors:
- Render errors — when a child throws during render it shows
fallback, or the default alert with Try again (which resets the boundary, then callsonRetry).onErroris called with the error and React'sErrorInfo. errorprop — when set, it parseserrorand rendersSpendingLimitAlert,InsufficientCreditsAlert, or a generic alert with the message instead ofchildren. Falsy renderschildren.
import { ErrorBoundary } from '@hyperyai/sdk';
// As a render-error boundary
<ErrorBoundary
fallback={(error, reset) => <button onClick={reset}>Something broke — retry</button>}
onError={(error, info) => Sentry.captureException(error)}
>
<Result />
</ErrorBoundary>
// As an API-error display
<ErrorBoundary error={lastError} onRetry={retry} onAddCredits={topUp} onUpgradeLimits={openLimits}>
<Result />
</ErrorBoundary>In @hyperyai/sdk 1.1.5 ErrorBoundary was a plain component that required
error and did not catch render errors. Existing error={…} usage still works.
useError
Local error state with parsing built in.
import { useError } from '@hyperyai/sdk';
const { error, setError, clearError, isAuth, isBillingRestriction } = useError();
try {
await doThing();
} catch (err) {
setError(err); // parsed into a ParsedError; falsy clears it
}parseError
import { parseError } from '@hyperyai/sdk';
const res = await fetch(url, init);
if (!res.ok) {
const parsed = parseError({ ...(await res.json()), status: res.status });
if (parsed.isPaymentMethodRequired) openCardModal();
}parseError(error: any): ParsedError accepts the response envelope
({ error: { code } }), an unwrapped error object ({ code }), or a thrown Error.
Include status for the status fallback. It returns:
Predicates and formatting
Each predicate is (error: any) => boolean and runs parseError on its input.
formatTimeUntilReset(resetsAt?: string): string formats an ISO time as
in 3h 12m / in 45m; returns soon if it's in the past and '' if omitted.
import { formatTimeUntilReset } from '@hyperyai/sdk';
formatTimeUntilReset(parsed.data.resetsAt); // "in 2h 5m"Mid-stream errors from streaming responses are covered in Streaming.