Skip to main content

Handling hook errors

All hooks return an errorHandling object in both loading and ready states. This ensures you can always display errors and offer recovery actions, even when data never loaded.

interface HookErrorHandling {
errors: SDKError[]
retryQueries: () => void
clearSubmitError: () => void
}

Multi-hook screens

When a screen pulls from more than one SDK hook (or mixes SDK hooks with additional @gusto/embedded-api-v-2026-02-01 queries), combine their error state into one banner and one retry/dismiss flow using composeErrorHandler / composeSubmitHandler. See Composing Multiple Hooks.

SDKError shape

interface SDKError {
category: 'api_error' | 'validation_error' | 'network_error' | 'internal_error'
message: string
httpStatus?: number
fieldErrors: SDKFieldError[]
raw?: unknown
}

interface SDKFieldError {
field: string
category: string
message: string
metadata?: Record<string, unknown>
}
CategoryWhat happenedWhat you should do
api_errorHTTP error from the Gusto API (422, 404, 409, etc.)Display error.message. For 422 responses, check error.fieldErrors for inline field-level messages. For 404/409, show a contextual message to the user.
validation_errorClient-side schema validation failed before the request was sentThis is likely an SDK bug. Display a generic error and report to Gusto.
network_errorNetwork connectivity failure (timeout, connection refused)Show retry UI using errorHandling.retryQueries(). Suggest the user check their connection.
internal_errorUnexpected SDK runtime errorDisplay a generic error and report to Gusto.

Recovery actions

  • retryQueries() — Retries all failed data-fetching queries. Dependent queries automatically re-trigger when their dependencies resolve.
  • clearSubmitError() — Clears the most recent form submission error from state.

Explicit query vs submit labels on each SDKError are not part of the type today; infer recovery from retryQueries (fetch) vs clearSubmitError (submit). A future revision may add structured discrimination.

Example: error display with retry

function EmployeeForm({ companyId }: { companyId: string }) {
const employeeDetails = useEmployeeDetailsForm({ companyId })

if (employeeDetails.isLoading) {
const { errors, retryQueries } = employeeDetails.errorHandling

if (errors.length > 0) {
return (
<div>
<p>Failed to load employee data.</p>
<ul>
{errors.map((error, i) => (
<li key={i}>{error.message}</li>
))}
</ul>
<button onClick={retryQueries}>Retry</button>
</div>
)
}

return <LoadingSpinner />
}

// ... render form
}

Handling submit errors

Submit errors (from API mutations) are also collected into errorHandling.errors. After a failed submission, you can display the error and let the user correct their input:

const { errors, clearSubmitError } = employeeDetails.errorHandling

{
errors.length > 0 && (
<div role="alert">
{errors.map((error, i) => (
<p key={i}>{error.message}</p>
))}
</div>
)
}

Field-level API errors (e.g., 422 responses with fieldErrors) are automatically synced to the corresponding form fields so they appear inline alongside client-side validation errors. When using SDKFormProvider, the provider handles this syncing via context. When using the formHookResult prop, each field resolves errors directly from formHookResult.errorHandling.errors — no provider is needed.

For a deeper look at the SDK's error architecture, see Error Handling in the React SDK and Observability.