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

# Errores

> Gestiona *APIError en el SDK de Go: estado, código y mensaje, errors.As y errors.Is, los códigos propios del SDK, las constantes Code, reintentos, timeouts y límites de tasa.

Todo fallo de la API, de red o local es un único tipo: `*squarecloud.APIError`. Inspecciónalo con `errors.As`.

```go theme={"system"}
package main

import (
	"context"
	"errors"
	"fmt"
	"os"

	"github.com/squarecloudofc/sdk-api-go/v3"
)

func main() {
	ctx := context.Background()
	c := squarecloud.New(os.Getenv("SQUARECLOUD_API_KEY"))
	appID := "abc123def456abc123def456"

	err := c.Apps.Start(ctx, appID)

	var apiErr *squarecloud.APIError
	if errors.As(err, &apiErr) {
		fmt.Println(apiErr.Status, apiErr.Code, apiErr.Message)
		fmt.Println(apiErr.Method, apiErr.Path) // "POST" "/v2/apps/<id>/start"
	}
}
```

## `APIError`

| Campo     | Tipo     | Descripción                                                                                                                                  |
| --------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| `Status`  | `int`    | Estado HTTP. `0` cuando no llegó ninguna respuesta (error de red, timeout, comprobación local)                                               |
| `Code`    | `string` | El código de error de la API, p. ej. `APP_NOT_FOUND`, o uno de los códigos del SDK. Compáralo con las [constantes `Code*`](#constantes-code) |
| `Message` | `string` | La explicación del servidor. `""` cuando el servidor solo envió un código                                                                    |
| `Method`  | `string` | Método HTTP de la llamada fallida                                                                                                            |
| `Path`    | `string` | Ruta de la URL de la llamada fallida, sin la query string                                                                                    |

`Unwrap()` devuelve la causa (el error de transporte, de decodificación o del context) para `NETWORK_ERROR`, `TIMEOUT` y el JSON no válido, o `nil`. `Error()` produce `squarecloud: <METHOD> <path>: HTTP <status> <CODE>: <message>`, sin `HTTP <status>` cuando el estado es `0` y sin `: <message>` cuando está vacío. Compara los campos, no este texto.

### Contexts cancelados y caducados

Una llamada cuyo `ctx` se cancela devuelve un `*APIError` con `NETWORK_ERROR`, y una cuyo deadline pasa devuelve `TIMEOUT`, ambos con estado `0`. Ambos envuelven el error del context:

```go theme={"system"}
_, err := c.Apps.Get(ctx, appID)

switch {
case errors.Is(err, context.Canceled):
	// you canceled ctx
case errors.Is(err, context.DeadlineExceeded):
	// the deadline of ctx, or the default one, passed
}
```

Algunos fallos no son `*APIError`:

* [`Realtime.Next`](/es/sdks/go/realtime#finalizar-el-stream) devuelve el `ctx.Err()` sin envolver cuando su `ctx` termina, e `io.EOF` cuando el stream termina con normalidad.
* Los problemas del lado del llamador son errores simples: un reader de subida `nil`, una URL de snapshot o una URL base que no se puede analizar, una entrada que `encoding/json` no puede codificar y un error del `io.Writer` que se pasa a `DownloadSnapshot`.

## Códigos del SDK

| Estado     | Código            | Constante           | Cuándo                                                                                                                                                                                                    |
| ---------- | ----------------- | ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 0          | `NETWORK_ERROR`   | `CodeNetworkError`  | Sin respuesta (DNS, conexión reiniciada, cuerpo cortado), o `ctx` cancelado. El error original está en `Unwrap()`                                                                                         |
| 0          | `TIMEOUT`         | `CodeTimeout`       | Sin respuesta antes del deadline (consulta [Timeouts](/es/sdks/go/client#timeouts))                                                                                                                       |
| 0          | `FILE_TOO_LARGE`  | `CodeFileTooLarge`  | Comprobación local: una subida de más de 100 MB o un `Files.Write` de más de 10 MB. No se envió nada                                                                                                      |
| 0          | `INVALID_ID`      | `CodeInvalidID`     | Comprobación local: un id vacío, `.` o `..`. No se envió nada                                                                                                                                             |
| 0          | `INVALID_API_KEY` | `CodeInvalidAPIKey` | Comprobación local: el cliente se creó con una clave vacía. Todas las llamadas salvo `Service.Status`. Solo en el SDK de Go                                                                               |
| cualquiera | `UNKNOWN_ERROR`   | `CodeUnknown`       | Una respuesta sin código (como la página de error de un proxy), con el `Status` real y el mensaje `HTTP <status>`. Un cuerpo 2xx que no es JSON tiene el mensaje `Invalid JSON in HTTP <status> response` |

## Constantes `Code*`

`Code` es un `string` simple. El paquete tiene una constante por cada código público de la API, con su nombre en estilo Go (`CodeAppNotFound` para `APP_NOT_FOUND`, `CodeInvalidID`, `CodeDNSFailed`, ...), además de los códigos propios del SDK indicados arriba.

La lista de códigos de la API crece. **Gestiona un código desconocido por su estado HTTP**:

```go theme={"system"}
func handle(err error) error {
	var apiErr *squarecloud.APIError
	if !errors.As(err, &apiErr) {
		return err
	}

	switch apiErr.Code {
	case squarecloud.CodeAppNotFound:
		return nil
	case squarecloud.CodeContainerAlreadyStarted:
		return nil // fine
	}
	switch {
	case apiErr.Status == 429:
		return retryLater()
	case apiErr.Status >= 500:
		return reportOutage(apiErr)
	}
	return err
}
```

## Errores de cualquier llamada

| Estado | Código                  | Cuándo                                                                                                                         |
| ------ | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| 401    | `ACCESS_DENIED`         | Clave de API ausente, desconocida, revocada o caducada                                                                         |
| 403    | `MISSING_SCOPE`         | A la clave le falta el scope de esta llamada. `Message` lo indica                                                              |
| 403    | `RESOURCE_NOT_ALLOWED`  | La clave está limitada a otras aplicaciones o bases de datos                                                                   |
| 403    | `PERMISSION_DENIED`     | Tu rol en el workspace no permite la acción                                                                                    |
| 404    | `ROUTE_NOT_FOUND`       | Ruta desconocida                                                                                                               |
| 429    | `RATE_LIMITED`          | Bloqueo de la cuenta, la clave o la IP (puede durar unos 30 min), y el límite de los endpoints de red y de `Account.Snapshots` |
| 429    | `KEEP_CALM`             | Demasiado rápido para esta ruta                                                                                                |
| 500    | `INTERNAL_SERVER_ERROR` | Error del servidor                                                                                                             |
| 503    | `DATABASE_UNAVAILABLE`  | La base de datos de la plataforma no está disponible. Es posible que una mutación ya se haya aplicado                          |

## Códigos de la API por grupo

<AccordionGroup>
  <Accordion title="No encontrado">
    `APP_NOT_FOUND`, `DATABASE_NOT_FOUND`, `WORKSPACE_NOT_FOUND`, `MEMBER_NOT_FOUND`, `FILE_NOT_FOUND`, `SNAPSHOT_NOT_FOUND`, `REPOSITORY_NOT_FOUND`, `BRANCH_NOT_FOUND`, `ROUTE_NOT_FOUND`
  </Accordion>

  <Accordion title="Validación">
    `INVALID_ACCESS_TOKEN`, `INVALID_AUTORESTART`, `INVALID_BRANCH_LENGTH`, `INVALID_CODE`, `INVALID_CONTENT`, `INVALID_CONTENT_TYPE`, `INVALID_DATABASE_TYPE`, `INVALID_DATABASE_VERSION`, `INVALID_DESCRIPTION`, `INVALID_DISPLAY_NAME`, `INVALID_DOMAIN`, `INVALID_ENCODING`, `INVALID_ENV_CONTENT`, `INVALID_FILE`, `INVALID_FILENAME`, `INVALID_FILTER`, `INVALID_GROUP`, `INVALID_ID`, `INVALID_INPUT`, `INVALID_JSON_BODY`, `INVALID_MEMORY`, `INVALID_NAME`, `INVALID_PARAMETERS`, `INVALID_PATH`, `INVALID_RESET_TYPE`, `INVALID_SCOPE`, `INVALID_SNAPSHOT_ID`, `INVALID_SUBDOMAIN`, `INVALID_TIME_RANGE`, `INVALID_VERSION_ID`, `MISSING_PARAMETERS`, `MISSING_REQUIRED_FIELDS`, `NO_UPDATE_DATA`, `VALIDATION_FAILED`, `VALIDATION_TIMEOUT`, `ENV_NAME_TOO_LONG`, `ENV_CONTENT_TOO_LONG`, `TOO_MANY_ENV_VARS`, `RESERVED_DOMAIN`, `CANNOT_SET_SUBDOMAIN`, `STATIC_APP_ENV_NOT_SUPPORTED`
  </Accordion>

  <Accordion title="Autenticación y permisos">
    `ACCESS_DENIED`, `MISSING_SCOPE`, `RESOURCE_NOT_ALLOWED`, `PERMISSION_DENIED`, `SCOPE_NOT_GRANTABLE`, `BLOCKED_PATH`, `UPGRADE_REQUIRED`
  </Accordion>

  <Accordion title="Límites y límites de tasa">
    `RATE_LIMITED`, `KEEP_CALM`, `APPLICATIONS_LIMIT_REACHED`, `WORKSPACE_LIMIT_REACHED`, `MEMBERS_LIMIT_REACHED`, `LOAD_BALANCER_LIMIT_REACHED`, `DAILY_SNAPSHOTS_LIMIT_REACHED`, `INSUFFICIENT_MEMORY`, `FILE_TOO_LARGE`, `PAYLOAD_TOO_LARGE`, `REALTIME_MAX_CONNECTIONS`, `REALTIME_MAX_CONNECTIONS_APP`, `AI_DAILY_LIMIT_REACHED`, `AI_MAX_CONCURRENT_STREAMS`, `AI_NO_PLAN_LIMIT_REACHED`
  </Accordion>

  <Accordion title="Contenedores (iniciar, detener, reiniciar)">
    `CONTAINER_ALREADY_STARTED`, `CONTAINER_ALREADY_STOPPED`, `CONTAINER_TEMPORARILY_SUSPENDED`, `CONTAINER_NOT_FOUND`, `CONTAINER_INSUFFICIENT_DISK_SPACE`, `CONTAINER_NETWORK_CONFLICT`, `ACTION_FAILED`, `DATABASE_NOT_RUNNING`
  </Accordion>

  <Accordion title="Subidas, archivos y commits">
    `UPLOAD_BUSY`, `UPLOAD_FAILED`, `UPLOAD_ABORTED`, `STORAGE_UPLOAD_FAILED`, `COMMIT_FAILED`, `READ_FAILED`, `SAVE_FAILED`, `RENAME_FAILED`, `DELETE_FAILED`, `REQUEST_ABORTED`, `EMPTY_RESPONSE`
  </Accordion>

  <Accordion title="Snapshots">
    `SNAPSHOT_FAILED`, `SNAPSHOT_PROCESSING`, `SNAPSHOT_RESTORE_FAILED`, `SNAPSHOT_DATABASE_MISMATCH`, `RESTORE_IN_PROGRESS`
  </Accordion>

  <Accordion title="Deploys y GitHub">
    `GIT_ALREADY_CONFIGURED`, `GIT_NOT_CONFIGURED`, `GITHUB_NOT_CONNECTED`, `REPOSITORY_BRANCH_ALREADY_CONFIGURED`, `REPOSITORY_NOT_AVAILABLE`, `REPOSITORY_PERMISSION_REQUIRED`, `FAILED_TO_FETCH`
  </Accordion>

  <Accordion title="Red y dominios">
    `ANALYTICS_BUSY`, `UNABLE_TO_FETCH_ANALYTICS`, `UNABLE_TO_FETCH_ERRORS`, `UNABLE_TO_FETCH_PERFORMANCE`, `DNS_FAILED`, `DOMAIN_ALREADY_EXISTS`, `NO_CUSTOM_DOMAIN`, `PURGE_CACHE_FAILED`, `LOGS_UNAVAILABLE`, `METRICS_NOT_SUPPORTED`
  </Accordion>

  <Accordion title="Bases de datos y workspaces">
    `DATABASE_CREATION_FAILED`, `DATABASE_UNAVAILABLE`, `RESET_FAILED`, `WORKSPACE_CREATION_FAILED`, `APP_ALREADY_IN_WORKSPACE`, `MEMBER_ALREADY_ADDED`, `CANNOT_EDIT_OWNER`, `CANNOT_INVITE_OWNER`, `CANNOT_LEAVE_OWNER`, `CONFLICTING_RESOURCES`
  </Accordion>

  <Accordion title="Plataforma">
    `INTERNAL_SERVER_ERROR`, `CLUSTER_MAINTENANCE_TRY_LATER`, `CLUSTER_SELECTION_FAILED`, `CLUSTER_TIMEOUT`, `CLUSTER_UNAVAILABLE`, `AI_UNAVAILABLE`
  </Accordion>

  <Accordion title="Obsoletos">
    `CodeRateLimit` (`RATE_LIMIT`) y `CodeRateLimitExceeded` (`RATE_LIMIT_EXCEEDED`) se siguen exportando, marcados como obsoletos: ahora la API responde `RATE_LIMITED` (`CodeRateLimited`) en ambos casos.
  </Accordion>
</AccordionGroup>

Los errores de `AI.Chat` usan en cambio códigos de OpenAI en minúsculas (`access_denied`, `rate_limit_exceeded`, `server_overloaded`, ...), que `Code` contiene tal cual. Consulta [IA](/es/sdks/go/ai#errores).

## Reintentos

El SDK solo reintenta lo que es seguro repetir, hasta `WithMaxRetries` veces (por defecto `2`, es decir, hasta 3 intentos):

| Se reintenta               | Métodos                                                                                     |
| -------------------------- | ------------------------------------------------------------------------------------------- |
| `NETWORK_ERROR`            | Solo `GET` (incluidas las aperturas de realtime y `DownloadSnapshot` antes de la respuesta) |
| 503 `UPLOAD_BUSY`          | Cualquier método                                                                            |
| 503 `ANALYTICS_BUSY`       | Cualquier método                                                                            |
| 503 `DATABASE_UNAVAILABLE` | Solo `GET`                                                                                  |

**Nunca** reintenta:

* `TIMEOUT`;
* ningún **429**: `RATE_LIMITED` puede ser un bloqueo de unos 30 minutos, y `KEEP_CALM` tampoco se reintenta;
* otros 5xx;
* los errores de IA.

Un 503 `DATABASE_UNAVAILABLE` puede llegar después de que una mutación ya se haya aplicado, así que el SDK no lo reintenta fuera de `GET`. Reintenta tú mismo tus mutaciones idempotentes si lo necesitas. Una subida solo se reintenta cuando su cuerpo se puede reproducir de nuevo: un `io.ReaderAt` de tamaño conocido, como un `*os.File` (consulta [Commit y subida](/es/sdks/go/commit_and_upload#entradas-aceptadas)).

La espera antes del reintento `n` (empezando en 0) es `min(8 s, 500 ms · 2^n) · U(0.5, 1)`: backoff exponencial con un jitter del 50% al 100%. Establece `WithMaxRetries(0)` para desactivar los reintentos.

## Timeouts

`WithTimeout` (30 s) solo se aplica cuando `ctx` no tiene deadline, y un único deadline cubre toda la llamada, reintentos y esperas de backoff incluidos. Consulta [Timeouts](/es/sdks/go/client#timeouts) para ver las llamadas con un mínimo de 2 minutos y las llamadas sin deadline por defecto. Un deadline que se supera devuelve `TIMEOUT` con estado `0`, nunca se reintenta y envuelve `context.DeadlineExceeded`.

## Límites de tasa

Cada cuenta tiene un límite de peticiones cada 60 segundos, fijado por su plan ([valores](/es/api-reference/limitations-and-restrictions)), y algunas rutas tienen el suyo propio:

* **429 `RATE_LIMITED`**: un bloqueo de la cuenta, la clave de API o la IP, que puede durar unos 30 minutos. También es el límite de los endpoints de red y de `Account.Snapshots`.
* **429 `KEEP_CALM`**: demasiadas llamadas a una misma ruta en poco tiempo.

El SDK nunca reintenta un 429. Reduce el ritmo y espera antes de volver a intentarlo.
