# Errores y códigos

> Todos los códigos que el SDK produce, qué significan y qué hacer con cada uno.


Todo error del SDK es un `AndcoError` con un `code` estable. Ramifica por `code`, nunca por `message`: el mensaje está escrito para que lo lea una persona y puede cambiar.

```ts
class AndcoError extends Error {
  readonly code: string;
  readonly status: number | undefined;
  readonly details: Readonly<Record<string, unknown>> | undefined;
}
```

## Códigos del SDK

Cada código enlaza a su definición en [`andco-sdk-js`](https://github.com/haulmer/andco-sdk-js).

| Código | Qué pasó | Qué hacer |
| --- | --- | --- |
| [`browser_required`](https://github.com/haulmer/andco-sdk-js/blob/main/packages/sdk/src/errors.ts#L228) | se pidió algo que solo existe en un navegador | usa el constructor del entorno correcto |
| [`browser_unavailable`](https://github.com/haulmer/andco-sdk-js/blob/main/packages/sdk/src/errors.ts#L229) | no hay `window` ni `document` | lo mismo |
| [`browser_forbidden`](https://github.com/haulmer/andco-sdk-js/blob/main/packages/sdk/src/errors.ts#L230) | se intentó crear una instancia de servidor en un navegador | mueve el secreto fuera del bundle |
| [`invalid_callback`](https://github.com/haulmer/andco-sdk-js/blob/main/packages/sdk/src/errors.ts#L231) | el callback no corresponde a la transacción: origen, `state` o antigüedad | vuelve a iniciar la autorización |
| [`invalid_configuration`](https://github.com/haulmer/andco-sdk-js/blob/main/packages/sdk/src/errors.ts#L232) | falta un valor o es inválido | corrige la configuración; es un fallo de arranque |
| [`handshake_timeout`](https://github.com/haulmer/andco-sdk-js/blob/main/packages/sdk/src/errors.ts#L233) | la superficie hospedada no respondió | comprueba el origen del widget y la red |
| [`invalid_iframe`](https://github.com/haulmer/andco-sdk-js/blob/main/packages/sdk/src/errors.ts#L234) | el elemento entregado al controlador no sirve | pasa un `HTMLIFrameElement` montado |
| [`invalid_intent_id`](https://github.com/haulmer/andco-sdk-js/blob/main/packages/sdk/src/errors.ts#L235) | el identificador de Intent está mal formado | revisa de dónde salió |
| [`invalid_response`](https://github.com/haulmer/andco-sdk-js/blob/main/packages/sdk/src/errors.ts#L236) | la respuesta del servidor no tenía lo necesario | reintenta; si persiste, es del lado del servidor |
| [`popup_blocked`](https://github.com/haulmer/andco-sdk-js/blob/main/packages/sdk/src/errors.ts#L237) | el navegador impidió abrir la ventana | vuelve a intentar dentro de un gesto, o usa redirección |
| [`popup_timeout`](https://github.com/haulmer/andco-sdk-js/blob/main/packages/sdk/src/errors.ts#L238) | la ventana quedó abierta sin resolverse | ofrece reintentar. No es una cancelación |
| [`presentation_unsupported`](https://github.com/haulmer/andco-sdk-js/blob/main/packages/sdk/src/errors.ts#L239) | el entorno no puede presentar así | usa `presentation: "redirect"` |
| [`same_origin_endpoint`](https://github.com/haulmer/andco-sdk-js/blob/main/packages/sdk/src/errors.ts#L240) | el iframe hospedado apunta a tu propio origen | corrige `endpointWidget` |
| [`session_missing`](https://github.com/haulmer/andco-sdk-js/blob/main/packages/sdk/src/errors.ts#L241) | la operación necesitaba sesión y no había | autentica primero |
| [`storage_failed`](https://github.com/haulmer/andco-sdk-js/blob/main/packages/sdk/src/errors.ts#L242) | el almacenamiento no pudo leer o escribir | revisa permisos o modo privado |
| [`untrusted_origin`](https://github.com/haulmer/andco-sdk-js/blob/main/packages/sdk/src/errors.ts#L243) | un mensaje vino de un origen que la instancia no permite | revisa `allowedPopupOrigins` |

## Códigos del protocolo

La superficie hospedada usa su propio vocabulario, que llega en el evento de error del botón. Son mayúsculas y forman un conjunto aparte: `HANDSHAKE_TIMEOUT`, `POPUP_BLOCKED`, `POPUP_NAVIGATION_FAILED`, `POPUP_TIMEOUT`, `UNTRUSTED_ORIGIN`, `INVALID_CLIENT_ID`, `INVALID_REDIRECT_TO`, `INVALID_SCOPE`, `UNSUPPORTED_PRESENTATION`, entre otros.

## Códigos del servidor de autorización

Cuando el fallo viene del servidor de autorización, el `code` es el de OAuth 2.1 tal cual: `access_denied`, `invalid_scope`, `invalid_grant`, `server_error`, `temporarily_unavailable`. Se pasan sin traducir para que puedas buscarlos en el RFC.

`access_denied` es el único que lleva una decisión de la persona, y por eso llega como cancelación y no como error.

## Cancelar no es fallar

```ts
const { data: session, error } = await andco.auth.signIn();
if (error) return showError(error);
if (!session) return; // closed the window
```

El SDK cuida esta distinción de su lado: un tiempo de espera agotado, una navegación fallida o un error del servidor llegan como errores, nunca como cancelación. Reportar una cancelación que nadie hizo es inventar una decisión.

## `status` y `details`

`status` es el código HTTP cuando el error vino de una respuesta. `details` trae lo que Andco explicó del fallo:

```ts
if (error.status === 403) {
  const missing = error.details?.["required_scope"];
  if (missing) await andco.auth.signIn({ scopes: [...session.scopes, String(missing)] });
}
```
