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

# Error handling

> SDK error codes and handling patterns

The SDK throws `NippyError` instances for operational errors. Use `NippyErrorCodes` to handle each case explicitly.

<Warning>
  Do not show internal error messages directly to users. Log `error.code`, `error.status`, and `error.traceId` for diagnostics.
</Warning>

## Base pattern

```typescript theme={null}
import { NippyError, NippyErrorCodes } from '@nippy/sdk'

try {
  await nippy.spin({ userId, campaignId })
} catch (error) {
  if (!(error instanceof NippyError)) throw error

  console.error(error.code, error.message, error.traceId)
}
```

## Error codes

<AccordionGroup>
  <Accordion title="COOLDOWN_ACTIVE">
    **Cause:** The user spun recently and the campaign cooldown is still active.

    **Fix:** Call `getState()` and show `nextEligibleAt`.

    ```typescript theme={null}
    if (error.code === NippyErrorCodes.COOLDOWN_ACTIVE) {
      const state = await nippy.getState({ userId, campaignId })
      return { reason: 'cooldown', nextEligibleAt: state.nextEligibleAt }
    }
    ```
  </Accordion>

  <Accordion title="SPIN_LIMIT_REACHED">
    **Cause:** The user used all allowed spins for the campaign.

    **Fix:** Disable the spin action and show the current state.

    ```typescript theme={null}
    if (error.code === NippyErrorCodes.SPIN_LIMIT_REACHED) {
      return { reason: 'spin_limit_reached' }
    }
    ```
  </Accordion>

  <Accordion title="CAMPAIGN_NOT_FOUND">
    **Cause:** The `campaignId` does not exist, belongs to another business, or the campaign is inactive.

    **Fix:** Verify the campaign ID and status before exposing it in your app.

    ```typescript theme={null}
    if (error.code === NippyErrorCodes.CAMPAIGN_NOT_FOUND) {
      return { reason: 'campaign_unavailable' }
    }
    ```
  </Accordion>

  <Accordion title="ALREADY_CLAIMED">
    **Cause:** `claim()` was called for a prize that was already claimed.

    **Fix:** Treat it as idempotent and show the already-claimed prize.

    ```typescript theme={null}
    if (error.code === NippyErrorCodes.ALREADY_CLAIMED) {
      return { reason: 'already_claimed' }
    }
    ```
  </Accordion>

  <Accordion title="RESERVATION_EXPIRED">
    **Cause:** The user did not claim the prize before the reservation expired.

    **Fix:** Inform the user that time ran out and call `getState()` again.

    ```typescript theme={null}
    if (error.code === NippyErrorCodes.RESERVATION_EXPIRED) {
      const state = await nippy.getState({ userId, campaignId })
      return { reason: 'reservation_expired', state }
    }
    ```
  </Accordion>

  <Accordion title="UNAUTHORIZED">
    **Cause:** The API key is missing or invalid.

    **Fix:** Check `NIPPY_API_KEY` and confirm it uses the correct prefix.

    ```typescript theme={null}
    if (error.code === NippyErrorCodes.UNAUTHORIZED) {
      logger.error('Invalid Nippy API key')
    }
    ```
  </Accordion>

  <Accordion title="FORBIDDEN">
    **Cause:** The API key does not have access to the requested resource.

    **Fix:** Check permissions and resource ownership in the console.

    ```typescript theme={null}
    if (error.code === NippyErrorCodes.FORBIDDEN) {
      logger.error('Nippy key does not have access to this resource')
    }
    ```
  </Accordion>

  <Accordion title="INVALID_PARAMETERS">
    **Cause:** Required fields are missing or a value has the wrong format.

    **Fix:** Validate data before calling the SDK.

    ```typescript theme={null}
    if (error.code === NippyErrorCodes.INVALID_PARAMETERS) {
      return { reason: 'invalid_request' }
    }
    ```
  </Accordion>

  <Accordion title="SERVER_ERROR">
    **Cause:** Nippy returned a 5xx error after automatic retries.

    **Fix:** Log `traceId`, show a generic state, and retry later.

    ```typescript theme={null}
    if (error.code === NippyErrorCodes.SERVER_ERROR) {
      logger.error('Nippy server error', { traceId: error.traceId })
      return { reason: 'temporary_error' }
    }
    ```
  </Accordion>
</AccordionGroup>

## NippyError properties

```typescript theme={null}
error.code      // string
error.message   // string
error.status    // number
error.traceId   // string | undefined
```
