> ## 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.

# Erreurs

> Gérez *APIError dans le SDK Go : statut, code et message, errors.As et errors.Is, les codes propres au SDK, les constantes Code, les nouvelles tentatives, les timeouts et les limites de débit.

Chaque échec de l'API, du réseau ou local est d'un seul type : `*squarecloud.APIError`. Inspectez-le avec `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`

| Champ     | Type     | Description                                                                                                                               |
| --------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| `Status`  | `int`    | Statut HTTP. `0` lorsqu'aucune réponse n'est arrivée (erreur réseau, timeout, vérification locale)                                        |
| `Code`    | `string` | Le code d'erreur de l'API, par ex. `APP_NOT_FOUND`, ou l'un des codes du SDK. Comparez-le avec les [constantes `Code*`](#constantes-code) |
| `Message` | `string` | L'explication du serveur. `""` lorsque le serveur n'a envoyé qu'un code                                                                   |
| `Method`  | `string` | Méthode HTTP de l'appel échoué                                                                                                            |
| `Path`    | `string` | Chemin de l'URL de l'appel échoué, sans la chaîne de requête                                                                              |

`Unwrap()` renvoie la cause (l'erreur de transport, de décodage ou de contexte) pour `NETWORK_ERROR`, `TIMEOUT` et un JSON invalide, sinon `nil`. `Error()` produit `squarecloud: <METHOD> <path>: HTTP <status> <CODE>: <message>`, sans `HTTP <status>` lorsque le statut vaut `0` et sans `: <message>` lorsqu'il est vide. Basez-vous sur les champs, pas sur ce texte.

### Contextes annulés et expirés

Un appel dont le `ctx` est annulé renvoie une `*APIError` avec `NETWORK_ERROR`, et un appel dont l'échéance est dépassée renvoie `TIMEOUT`, tous deux avec le statut `0`. Ils encapsulent l'erreur du contexte :

```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
}
```

Quelques échecs ne sont pas des `*APIError` :

* [`Realtime.Next`](/fr/sdks/go/realtime#fin-du-flux) renvoie le `ctx.Err()` brut lorsque son `ctx` se termine, et `io.EOF` lorsque le flux se termine normalement.
* Les problèmes du côté de l'appelant sont des erreurs ordinaires : un reader d'envoi `nil`, une URL de snapshot ou une URL de base qui ne peut pas être analysée, une entrée que `encoding/json` ne peut pas encoder, et une erreur du `io.Writer` passé à `DownloadSnapshot`.

## Codes du SDK

| Statut | Code              | Constante           | Quand                                                                                                                                                                                                   |
| ------ | ----------------- | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 0      | `NETWORK_ERROR`   | `CodeNetworkError`  | Aucune réponse (DNS, connexion réinitialisée, corps tronqué), ou `ctx` annulé. L'erreur d'origine se trouve dans `Unwrap()`                                                                             |
| 0      | `TIMEOUT`         | `CodeTimeout`       | Aucune réponse avant l'échéance (voir [Timeouts](/fr/sdks/go/client#timeouts))                                                                                                                          |
| 0      | `FILE_TOO_LARGE`  | `CodeFileTooLarge`  | Vérification locale : un envoi de plus de 100 Mo ou un `Files.Write` de plus de 10 Mo. Rien n'a été envoyé                                                                                              |
| 0      | `INVALID_ID`      | `CodeInvalidID`     | Vérification locale : un identifiant vide, `.` ou `..`. Rien n'a été envoyé                                                                                                                             |
| 0      | `INVALID_API_KEY` | `CodeInvalidAPIKey` | Vérification locale : le client a été créé avec une clé vide. Tous les appels sauf `Service.Status`. Uniquement dans le SDK Go                                                                          |
| tous   | `UNKNOWN_ERROR`   | `CodeUnknown`       | Une réponse sans code (comme une page d'erreur de proxy), avec le vrai `Status` et le message `HTTP <status>`. Un corps 2xx qui n'est pas du JSON a le message `Invalid JSON in HTTP <status> response` |

## Constantes `Code*`

`Code` est une simple `string`. Le paquet contient une constante par code public de l'API, nommée d'après lui dans le style Go (`CodeAppNotFound` pour `APP_NOT_FOUND`, `CodeInvalidID`, `CodeDNSFailed`, ...), plus les codes propres au SDK ci-dessus.

La liste des codes de l'API s'allonge. **Traitez un code inconnu d'après son statut 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
}
```

## Erreurs de n'importe quel appel

| Statut | Code                    | Quand                                                                                                                         |
| ------ | ----------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| 401    | `ACCESS_DENIED`         | Clé API manquante, inconnue, révoquée ou expirée                                                                              |
| 403    | `MISSING_SCOPE`         | Il manque à la clé le scope de cet appel. `Message` le nomme                                                                  |
| 403    | `RESOURCE_NOT_ALLOWED`  | La clé est limitée à d'autres applications ou bases de données                                                                |
| 403    | `PERMISSION_DENIED`     | Votre rôle dans le workspace n'autorise pas l'action                                                                          |
| 404    | `ROUTE_NOT_FOUND`       | Route inconnue                                                                                                                |
| 429    | `RATE_LIMITED`          | Blocage du compte, de la clé ou de l'IP (peut durer environ 30 min), et limite des endpoints réseau et de `Account.Snapshots` |
| 429    | `KEEP_CALM`             | Trop rapide pour cette route                                                                                                  |
| 500    | `INTERNAL_SERVER_ERROR` | Erreur du serveur                                                                                                             |
| 503    | `DATABASE_UNAVAILABLE`  | La base de données de la plateforme est indisponible. Une mutation a peut-être déjà été appliquée                             |

## Codes de l'API par groupe

<AccordionGroup>
  <Accordion title="Introuvable">
    `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="Validation">
    `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="Authentification et permissions">
    `ACCESS_DENIED`, `MISSING_SCOPE`, `RESOURCE_NOT_ALLOWED`, `PERMISSION_DENIED`, `SCOPE_NOT_GRANTABLE`, `BLOCKED_PATH`, `UPGRADE_REQUIRED`
  </Accordion>

  <Accordion title="Limites et limites de débit">
    `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="Conteneurs (démarrage, arrêt, redémarrage)">
    `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="Envois, fichiers et 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 et 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="Réseau et domaines">
    `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 données et 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="Plateforme">
    `INTERNAL_SERVER_ERROR`, `CLUSTER_MAINTENANCE_TRY_LATER`, `CLUSTER_SELECTION_FAILED`, `CLUSTER_TIMEOUT`, `CLUSTER_UNAVAILABLE`, `AI_UNAVAILABLE`
  </Accordion>

  <Accordion title="Dépréciés">
    `CodeRateLimit` (`RATE_LIMIT`) et `CodeRateLimitExceeded` (`RATE_LIMIT_EXCEEDED`) sont toujours exportées, marquées comme dépréciées : l'API répond désormais `RATE_LIMITED` (`CodeRateLimited`) pour les deux.
  </Accordion>
</AccordionGroup>

Les erreurs de `AI.Chat` utilisent plutôt les codes OpenAI en minuscules (`access_denied`, `rate_limit_exceeded`, `server_overloaded`, ...), que `Code` reprend tels quels. Voir [IA](/fr/sdks/go/ai#erreurs).

## Nouvelles tentatives

Le SDK ne réessaie que ce qui peut être répété sans risque, jusqu'à `WithMaxRetries` fois (par défaut `2`, soit jusqu'à 3 tentatives) :

| Réessayé                   | Méthodes                                                                                   |
| -------------------------- | ------------------------------------------------------------------------------------------ |
| `NETWORK_ERROR`            | `GET` uniquement (ouvertures du temps réel et `DownloadSnapshot` avant la réponse compris) |
| 503 `UPLOAD_BUSY`          | Toute méthode                                                                              |
| 503 `ANALYTICS_BUSY`       | Toute méthode                                                                              |
| 503 `DATABASE_UNAVAILABLE` | `GET` uniquement                                                                           |

Il ne réessaie **jamais** :

* `TIMEOUT` ;
* aucun **429** : `RATE_LIMITED` peut être un blocage d'environ 30 minutes, et `KEEP_CALM` n'est pas réessayé non plus ;
* les autres 5xx ;
* les erreurs d'IA.

503 `DATABASE_UNAVAILABLE` peut arriver alors qu'une mutation a déjà été appliquée : le SDK ne la réessaie donc pas en dehors de `GET`. Réessayez vous-même vos mutations idempotentes si nécessaire. Un envoi n'est réessayé que si son corps peut être rejoué : un `io.ReaderAt` de taille connue, comme un `*os.File` (voir [Commit et envoi](/fr/sdks/go/commit_and_upload#entrées-acceptées)).

L'attente avant la nouvelle tentative `n` (à partir de 0) est `min(8 s, 500 ms · 2^n) · U(0.5, 1)` : un backoff exponentiel avec un jitter de 50 % à 100 %. Définissez `WithMaxRetries(0)` pour désactiver les nouvelles tentatives.

## Timeouts

`WithTimeout` (30 s) ne s'applique que lorsque `ctx` n'a pas d'échéance, et une seule échéance couvre tout l'appel, nouvelles tentatives et attentes de backoff comprises. Voir [Timeouts](/fr/sdks/go/client#timeouts) pour les appels avec un plancher de 2 minutes et les appels sans échéance par défaut. Une échéance dépassée renvoie `TIMEOUT` avec le statut `0`, n'est jamais réessayée et encapsule `context.DeadlineExceeded`.

## Limites de débit

Chaque compte dispose d'une limite de requêtes par 60 secondes, fixée par son plan ([valeurs](/fr/api-reference/limitations-and-restrictions)), et certaines routes ont la leur :

* **429 `RATE_LIMITED`** : un blocage du compte, de la clé API ou de l'IP, qui peut durer environ 30 minutes. C'est aussi la limite des endpoints réseau et de `Account.Snapshots`.
* **429 `KEEP_CALM`** : trop d'appels vers une même route en peu de temps.

Le SDK ne réessaie jamais un 429. Ralentissez, et attendez avant de réessayer.
