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

# Errori

> Gestisci *APIError nell'SDK Go: status, codice e messaggio, errors.As ed errors.Is, i codici propri dell'SDK, le costanti Code, retry, timeout e rate limit.

Ogni errore dell'API, di rete e locale è di un unico tipo: `*squarecloud.APIError`. Ispezionalo 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     | Descrizione                                                                                                                                 |
| --------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `Status`  | `int`    | Status HTTP. `0` quando non è arrivata alcuna risposta (errore di rete, timeout, controllo locale)                                          |
| `Code`    | `string` | Il codice di errore dell'API, ad es. `APP_NOT_FOUND`, oppure uno dei codici dell'SDK. Confrontalo con le [costanti `Code*`](#costanti-code) |
| `Message` | `string` | La spiegazione del server. `""` quando il server ha inviato solo un codice                                                                  |
| `Method`  | `string` | Metodo HTTP della chiamata fallita                                                                                                          |
| `Path`    | `string` | Percorso dell'URL della chiamata fallita, senza query string                                                                                |

`Unwrap()` restituisce la causa (l'errore di trasporto, di decodifica o del context) per `NETWORK_ERROR`, `TIMEOUT` e JSON non valido, altrimenti `nil`. `Error()` produce `squarecloud: <METHOD> <path>: HTTP <status> <CODE>: <message>`, senza `HTTP <status>` quando lo status è `0` e senza `: <message>` quando è vuoto. Basati sui campi, non su questo testo.

### Context annullati e scaduti

Una chiamata il cui `ctx` viene annullato restituisce un `*APIError` con `NETWORK_ERROR`, e una la cui scadenza passa restituisce `TIMEOUT`, entrambi con status `0`. Entrambi fanno l'unwrap all'errore 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
}
```

Alcuni fallimenti non sono `*APIError`:

* [`Realtime.Next`](/it/sdks/go/realtime#terminare-il-flusso) restituisce il semplice `ctx.Err()` quando il suo `ctx` termina, e `io.EOF` quando il flusso termina normalmente.
* I problemi dal lato del chiamante sono errori semplici: un reader di upload `nil`, un URL di snapshot o un URL base che non può essere analizzato, un input che `encoding/json` non riesce a codificare e un errore dell'`io.Writer` passato a `DownloadSnapshot`.

## Codici dell'SDK

| Status    | Codice            | Costante            | Quando                                                                                                                                                                                                      |
| --------- | ----------------- | ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 0         | `NETWORK_ERROR`   | `CodeNetworkError`  | Nessuna risposta (DNS, connessione reimpostata, body troncato), oppure `ctx` annullato. L'errore originale è in `Unwrap()`                                                                                  |
| 0         | `TIMEOUT`         | `CodeTimeout`       | Nessuna risposta prima della scadenza (vedi [Timeout](/it/sdks/go/client#timeout))                                                                                                                          |
| 0         | `FILE_TOO_LARGE`  | `CodeFileTooLarge`  | Controllo locale: un upload oltre 100 MB o un `Files.Write` oltre 10 MB. Non è stato inviato nulla                                                                                                          |
| 0         | `INVALID_ID`      | `CodeInvalidID`     | Controllo locale: un id vuoto, `.` o `..`. Non è stato inviato nulla                                                                                                                                        |
| 0         | `INVALID_API_KEY` | `CodeInvalidAPIKey` | Controllo locale: il client è stato creato con una chiave vuota. Ogni chiamata tranne `Service.Status`. Solo nell'SDK Go                                                                                    |
| qualsiasi | `UNKNOWN_ERROR`   | `CodeUnknown`       | Una risposta senza codice (come la pagina di errore di un proxy), con lo `Status` reale e il messaggio `HTTP <status>`. Un body 2xx che non è JSON ha il messaggio `Invalid JSON in HTTP <status> response` |

## Costanti `Code*`

`Code` è una semplice `string`. Il package ha una costante per ogni codice pubblico dell'API, con il nome in stile Go (`CodeAppNotFound` per `APP_NOT_FOUND`, `CodeInvalidID`, `CodeDNSFailed`, ...), più i codici propri dell'SDK elencati sopra.

L'elenco dei codici dell'API cresce. **Gestisci un codice sconosciuto in base al suo status 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
}
```

## Errori di qualsiasi chiamata

| Status | Codice                  | Quando                                                                                                                          |
| ------ | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
| 401    | `ACCESS_DENIED`         | Chiave API mancante, sconosciuta, revocata o scaduta                                                                            |
| 403    | `MISSING_SCOPE`         | Alla chiave manca lo scope di questa chiamata. `Message` lo indica                                                              |
| 403    | `RESOURCE_NOT_ALLOWED`  | La chiave è limitata ad altre app o database                                                                                    |
| 403    | `PERMISSION_DENIED`     | Il tuo ruolo nel workspace non consente l'azione                                                                                |
| 404    | `ROUTE_NOT_FOUND`       | Route sconosciuta                                                                                                               |
| 429    | `RATE_LIMITED`          | Blocco dell'account, della chiave o dell'IP (può durare circa 30 min), e limite degli endpoint di rete e di `Account.Snapshots` |
| 429    | `KEEP_CALM`             | Troppo veloce per questa route                                                                                                  |
| 500    | `INTERNAL_SERVER_ERROR` | Errore del server                                                                                                               |
| 503    | `DATABASE_UNAVAILABLE`  | Il database della piattaforma non è disponibile. Una mutazione potrebbe essere già stata applicata                              |

## Codici dell'API per gruppo

<AccordionGroup>
  <Accordion title="Non trovato">
    `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="Validazione">
    `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="Autenticazione e permessi">
    `ACCESS_DENIED`, `MISSING_SCOPE`, `RESOURCE_NOT_ALLOWED`, `PERMISSION_DENIED`, `SCOPE_NOT_GRANTABLE`, `BLOCKED_PATH`, `UPGRADE_REQUIRED`
  </Accordion>

  <Accordion title="Limiti e rate limit">
    `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="Container (avvio, arresto, riavvio)">
    `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="Upload, file e commit">
    `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="Snapshot">
    `SNAPSHOT_FAILED`, `SNAPSHOT_PROCESSING`, `SNAPSHOT_RESTORE_FAILED`, `SNAPSHOT_DATABASE_MISMATCH`, `RESTORE_IN_PROGRESS`
  </Accordion>

  <Accordion title="Deploy e 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="Rete e domini">
    `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="Database e workspace">
    `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="Piattaforma">
    `INTERNAL_SERVER_ERROR`, `CLUSTER_MAINTENANCE_TRY_LATER`, `CLUSTER_SELECTION_FAILED`, `CLUSTER_TIMEOUT`, `CLUSTER_UNAVAILABLE`, `AI_UNAVAILABLE`
  </Accordion>

  <Accordion title="Deprecati">
    `CodeRateLimit` (`RATE_LIMIT`) e `CodeRateLimitExceeded` (`RATE_LIMIT_EXCEEDED`) sono ancora esportati, contrassegnati come deprecati: l'API ora risponde `RATE_LIMITED` (`CodeRateLimited`) per entrambi.
  </Accordion>
</AccordionGroup>

Gli errori di `AI.Chat` usano invece i codici minuscoli di OpenAI (`access_denied`, `rate_limit_exceeded`, `server_overloaded`, ...), che `Code` riporta testualmente. Vedi [AI](/it/sdks/go/ai#errori).

## Retry

L'SDK ripete solo ciò che è sicuro ripetere, fino a `WithMaxRetries` volte (predefinito `2`, quindi fino a 3 tentativi):

| Ripetuto                   | Metodi                                                                               |
| -------------------------- | ------------------------------------------------------------------------------------ |
| `NETWORK_ERROR`            | Solo `GET` (comprese le aperture realtime e `DownloadSnapshot` prima della risposta) |
| 503 `UPLOAD_BUSY`          | Qualsiasi metodo                                                                     |
| 503 `ANALYTICS_BUSY`       | Qualsiasi metodo                                                                     |
| 503 `DATABASE_UNAVAILABLE` | Solo `GET`                                                                           |

**Non** ripete mai:

* `TIMEOUT`;
* qualsiasi **429**: `RATE_LIMITED` può essere un blocco di circa 30 minuti, e nemmeno `KEEP_CALM` viene ripetuto;
* gli altri 5xx;
* gli errori dell'AI.

503 `DATABASE_UNAVAILABLE` può arrivare dopo che una mutazione è già stata applicata, quindi l'SDK non lo ripete al di fuori di `GET`. Ripeti tu le tue mutazioni idempotenti, se necessario. Un upload viene ripetuto solo quando il suo body può essere riprodotto: un `io.ReaderAt` con dimensione nota, come un `*os.File` (vedi [Commit e upload](/it/sdks/go/commit_and_upload#input-accettati)).

L'attesa prima del retry `n` (a partire da 0) è `min(8 s, 500 ms · 2^n) · U(0.5, 1)`: backoff esponenziale con jitter dal 50% al 100%. Imposta `WithMaxRetries(0)` per disattivare i retry.

## Timeout

`WithTimeout` (30 s) si applica solo quando `ctx` non ha una scadenza, e una sola scadenza copre l'intera chiamata, compresi i retry e le attese di backoff. Vedi [Timeout](/it/sdks/go/client#timeout) per le chiamate con una soglia minima di 2 minuti e quelle senza scadenza predefinita. Una scadenza superata restituisce `TIMEOUT` con status `0`, non viene mai ripetuta e fa l'unwrap a `context.DeadlineExceeded`.

## Rate limit

Ogni account ha un limite di richieste ogni 60 secondi, stabilito dal suo piano ([valori](/it/api-reference/limitations-and-restrictions)), e alcune route hanno un limite proprio:

* **429 `RATE_LIMITED`**: un blocco dell'account, della chiave API o dell'IP, che può durare circa 30 minuti. È anche il limite degli endpoint di rete e di `Account.Snapshots`.
* **429 `KEEP_CALM`**: troppe chiamate a una route in poco tempo.

L'SDK non ripete mai un 429. Rallenta e attendi prima di riprovare.
