spicyapiDocumentazione
Riferimento
Contenuto principale

Errori

Tutti i codici applicativi, stati HTTP, azioni da intraprendere e tentativi che vale la pena ripetere.

Quando una chiamata non va a buon fine, la risposta comunica due cose: un numero (code) che indica il tipo di problema e una frase (msg) da leggere. Questa pagina elenca tutti i numeri, il loro significato e quando riprovare può aiutare. Se cerchi soltanto una soluzione rapida a un sintomo, comincia da Risoluzione dei problemi.

Struttura della risposta

Le risposte JSON dell'API nativa delle attività usano questa struttura esterna: gli stessi quattro campi intorno a ogni risposta. Interpreta le risposte compatibili /v1 e SSE secondo i rispettivi protocolli:

{ "code": 200, "msg": "success", "data": { }, "request_id": "req_…" }
  • code è un codice applicativo, non uno stato HTTP. 200 significa successo.
  • In caso di errore, data è assente (non null): ricevi code, msg e request_id. L'unica eccezione: alcuni errori della libreria di asset aggiungono data.reason.
  • msg è per le persone: la formulazione può cambiare, così come la lingua (vedi sotto). Non basare mai la logica su questo testo; basala su code.
  • request_id viene restituito anche nell'header X-Request-Id. Citalo quando segnali un problema.

Anche lo stato HTTP è impostato correttamente. Basa la logica sull'uno o sull'altro, ma soltanto su uno: i due differiscono per i codici specifici (un saldo insufficiente è code: 40201 con HTTP 402) e mescolarli prima o poi fa perdere un caso.

Consigliamo di basare la logica su code. 404 può dire soltanto «non trovato»; 40201 dice che il saldo disponibile è insufficiente.

Non tutte le risposte sono JSON

Una risposta non interpretabile come JSON, per esempio una pagina HTML o di testo della rete perimetrale, non ha code. Gestiscila in base allo stato HTTP, registra l'inizio del corpo e non riprovare alla cieca un 403: potrebbe essere un blocco regionale che nessun tentativo può cambiare.

Lingua dei messaggi di errore

Il testo leggibile degli errori può arrivare nella lingua che scegli. La lingua predefinita è l'inglese. Soltanto questi tre elementi seguono la scelta:

  • msg nella struttura esterna della risposta;
  • errorMessage di un'attività fallita, sia in recordInfo sia nei callback webhook (con un'eccezione, sotto);
  • error.message negli oggetti di errore degli endpoint compatibili, nel formato OpenAI, Anthropic o Google Gemini (/v1beta).

Codici di lingua supportati: en, ja, ko, de, fr, es, pt-BR, ru, it, pl.

L'eccezione: quando il modello stesso fornisce un motivo specifico del fallimento, per esempio un'immagine di riferimento illeggibile o una restrizione sul copyright dell'audio generato, errorMessage riporta quel motivo in inglese, rimuovendo dettagli interni come nomi di servizi, host, URL e ID di richiesta, così puoi vedere cosa sia realmente accaduto. Motivi relativi all'account o alla fatturazione non vengono mai riportati; ricevi invece un nostro messaggio. Content-Language continua a descrivere msg ed errorCode resta sempre il valore su cui basare la logica.

Scegliere la lingua

Vince il primo di questi criteri applicabile:

  1. L'header di richiesta Accept-Language, soltanto per quella richiesta. Si applicano i pesi HTTP standard: scegliamo la lingua riconosciuta con il valore q più alto. Le varianti regionali vengono ricondotte alla lingua: de-DE vale come de e pt-PT come pt-BR. Il cinese non è offerto, quindi ogni tag cinese non trova corrispondenza e viene provato il candidato successivo. * significa nessuna preferenza.
  2. L'impostazione del tuo account, sotto Impostazioni → Lingua degli errori API nella console. Copre ogni richiesta effettuata con una delle chiavi API dell'account priva di un Accept-Language riconoscibile. La maggior parte degli SDK lato server e curl non invia questo header di default, quindi è l'opzione su cui si basa la maggior parte delle integrazioni. I callback webhook non hanno un header di richiesta su cui basarsi, quindi seguono sempre l'impostazione dell'account.
  3. Altrimenti, inglese.

Le risposte di errore includono un header Content-Language che indica la lingua in cui è stato effettivamente scritto il messaggio.

Cambia soltanto il testo leggibile dalle persone

code, errorCode, type e code compatibili con OpenAI, code e status compatibili con Gemini e ogni nome di campo restano gli stessi in ogni lingua. Basa la logica sui codici e non analizzare mai la formulazione; registra code e request_id nei log e negli avvisi.

Esempio

Lo stesso 40201 (saldo insufficiente) viene restituito in inglese per impostazione predefinita:

Default
HTTP/1.1 402 Payment Required
Content-Language: en

{ "code": 40201, "msg": "Insufficient balance", "request_id": "req_…" }

Aggiungi un header Accept-Language: ja; il resto è la stessa chiamata di creazione dell'attività dell'avvio rapido:

curl -X POST https://api.spicyapi.ai/api/v1/jobs/createTask \
  -H "Authorization: Bearer $SPICY_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $SPICY_IDEMPOTENCY_KEY" \
  -H "Accept-Language: ja" \
  --data "$TASK_PAYLOAD"

msg ora è in giapponese; code è ancora 40201:

Accept-Language: ja
HTTP/1.1 402 Payment Required
Content-Language: ja

{ "code": 40201, "msg": "残高が不足しています", "request_id": "req_…" }

Codici generali

codeHTTPSignificatoRiprovare?
200200Successo—
400400Richiesta malformata o parametri non validiNo. Correggi e reinvia
40003400Dimensione, tipo o contenuto del file caricato non corrispondono a quanto dichiarato nella richiesta dell'URL di caricamentoNo. Richiedi un nuovo URL con contentType e dimensione corretti, carica nuovamente e conferma
40004400La richiesta è valida, ma questo modello non può gestire questa combinazione di impostazioni adessoNo. Cambia l'impostazione indicata in msg (per esempio risoluzione o rapporto) oppure scegli un altro modello. Consulta sotto
401401Autenticazione fallita; msg indica quale dei tre casiNo. Correggi l'header o la chiave
403403Non consentito. In una chiamata API con chiave valida, l'account è soggetto a restrizioni: vedi sotto. Un'email non verificata restituisce invece 40310No
404404Non esiste (oppure non è tuo)No
409409Conflitto con lo stato attualeA volte, vedi sotto
40901409Preventivo scaduto, richiesta cambiata o prezzo cambiatoOttieni e conferma un nuovo preventivo; recupera le richieste accettate con la chiave di idempotenza originale
413413Il corpo della richiesta supera il limite di dimensione accettato dal serverNo. Riduci il corpo oppure carica i media con il percorso dedicato
429429Troppe richiesteDi solito. Attendi secondo Retry-After. Un tipo di 429 non riguarda la velocità: consulta Limiti di frequenza
500500Qualcosa non funziona dalla nostra parteSì. Attendi progressivamente e riprova
503503Un servizio di supporto (preventivi, archiviazione di immagini inline, caricamenti multimediali) non è disponibile per breve tempoSì. Attendi Retry-After se presente e riprova; su createTask, con lo stesso Idempotency-Key. Se persiste senza Retry-After, segui msg (per esempio usa un URL HTTPS pubblico invece di un'immagine inline)

503 e 50301 condividono uno stato HTTP ma sono codici applicativi diversi: 503 riguarda un servizio di supporto, 50301 il modello stesso.

Distinguere i tre 401

Tre errori molto diversi portano tutti a 401 in autenticazione e hanno soluzioni diverse. code resta 401 in ogni caso; è msg a distinguerli.

msg (inglese)Cosa è successo davveroCosa cambiare
Missing credentials: send your API key in the Authorization header as "Bearer sk-spicy-..." (negli endpoint compatibili /v1 e /v1beta: …as "Authorization: Bearer sk-spicy-...", x-api-key, or x-goog-api-key)La richiesta è arrivata senza alcuna credenzialeIl client sta omettendo l'header. Controlla interceptor, proxy e caricamento della variabile d'ambiente nel processo in esecuzione
API key format is not recognized: keys begin with sk-spicy-È arrivata una credenziale, ma non è una delle nostreHai incollato una sessione della console o la chiave di un altro servizio. Le chiavi SpicyAPI iniziano con sk-spicy-; echo "${SPICY_API_KEY:0:9}" deve mostrare esattamente quel prefisso
Credentials are invalid or expiredLa chiave ha il nostro formato, ma non esiste, è revocata o ha superato la scadenzaControllala nella pagina delle chiavi API; se revocata o scaduta, creane una sostitutiva. Riprovare non produrrà mai un 200

Il caso centrale viene deciso senza una ricerca nel database, ed è per questo che comunica qualcosa che il terzo non può: la stringa non aveva mai alcuna possibilità di funzionare. Il terzo messaggio è intenzionalmente identico per chiavi inesistenti, revocate e scadute, così la risposta non permette di scoprire quali chiavi esistano.

Qui queste stringhe sono mostrate in inglese; msg segue la selezione della lingua descritta sopra e la sua formulazione non è un contratto. Basa la logica su code e tratta il messaggio come qualcosa da leggere, mostrare o registrare, non da confrontare per prendere decisioni.

L'account non può chiamare l'API

In entrambi questi casi la chiave stessa è valida; è l'account a non poter ancora chiamare l'API.

40310 significa che l'email non è stata verificata. Il messaggio è Verify your account email to run models and call the API…:

  1. Apri la console. Un banner in alto indica che l'email non è verificata e permette di inviare nuovamente l'email di verifica.
  2. Segui il link nell'email di verifica (controlla la cartella spam).
  3. Riprova la stessa chiamata. Non serve una nuova chiave.

La console continua a funzionare mentre l'API è bloccata, quindi puoi ancora vedere la cronologia.

Un semplice 403 con This account cannot call the API right now significa che l'account è soggetto a restrizioni. Contatta l'assistenza con il request_id.

Saldo e limiti

codeHTTPSignificatoCosa fare
40201402Il saldo non copre il costo stimatoAggiungi fondi sotto Fatturazione nella console; consulta Configurazione dell'account. Controlla GET /api/v1/chat/credit
40202402È stato raggiunto un limite di spesaLeggi msg per sapere quale. Limite della chiave (This key has reached its …): aumentalo nella pagina delle chiavi API oppure attendi l'azzeramento. Limite del team (Workspace … budget has been reached): chiedi a proprietario, amministratore o responsabile del budget di aumentarlo nella pagina Team. Limite della piattaforma (Platform quota for today is exhausted…): non puoi cambiarlo; riprova dopo le 00:00 UTC. I limiti giornalieri si azzerano alle 00:00 UTC, quelli mensili il primo del mese alle 00:00 UTC; un limite totale non si azzera mai

Nessuno dei due merita nuovi tentativi: saldi e limiti non cambiano perché chiedi di nuovo. Consulta Limiti di spesa.

Permessi

codeHTTPSignificatoCosa fare
40301403Questa chiave non può chiamare questo modelloAmplia l'elenco dei modelli consentiti della chiave nella console
40302403L'indirizzo sorgente non è nell'elenco degli IP consentiti della chiaveAggiungilo oppure rimuovi la restrizione IP della chiave
40303403Il servizio non è offerto in questa regioneNon può essere aggirato. Consulta Dove operiamo
40310403L'email dell'account non è ancora verificata, quindi non può eseguire modelliApri il link di verifica nell'email di registrazione oppure inviane una nuova dalla console. La chiave funziona appena l'indirizzo è verificato, senza modifiche da parte tua

Nessuno di questi errori può essere risolto riprovando.

Codici di motivo della libreria di asset

Gli errori sugli endpoint della libreria di asset (/api/v1/assets…) possono aggiungere alla solita busta un campo leggibile da macchina, data.reason:

{ "code": 400, "msg": "…", "data": { "reason": "asset_library_locked" }, "request_id": "req_…" }

code, lo stato HTTP, msg e l'errorCode dell'attività sono uguali con o senza motivo, quindi la gestione esistente continua a funzionare. Il motivo compare solo sugli endpoint della libreria di asset, mai sugli altri endpoint nativi, e il livello compatibile con OpenAI e gli altri livelli di compatibilità mantengono le proprie forme di errore. L'elenco può crescere: tratta un valore sconosciuto come un motivo assente.

data.reasonHTTPSignificatoCosa fare
asset_library_locked400La libreria di asset si apre con la prima ricaricaRicarica. Gli asset già salvati restano utilizzabili
asset_library_full400La quota di archiviazione (elementi o byte) è pienaElimina asset o passa a un piano superiore; GET /api/v1/assets/usage mostra il successivo
asset_file_too_large400Il file supera il limite per file del pianoUsa un file più piccolo o passa a un piano superiore
asset_daily_limit429È stato raggiunto il numero giornaliero di nuovi assetAttendi le 00:00 UTC e rispetta Retry-After
asset_storage_full503L'archiviazione dell'intera piattaforma è pienaRiprova più tardi
asset_permission_denied403Il tuo ruolo nel team non consente questa modificaChiedi a un proprietario o amministratore del team, oppure usa una chiave il cui creatore abbia un ruolo con questo permesso
asset_workspace_forbidden403Questa chiave non raggiunge quella libreria, per esempio perché il membro che ha creato la chiave di team ha lasciato il teamCrea una nuova chiave nello spazio di lavoro che ti serve

Disponibilità dei modelli

codeHTTPSignificatoRiprovare?
50301503Il modello non è attualmente disponibileSì. Riprova più tardi oppure scegli un altro modello
50302503Una richiesta di testo, con o senza streaming, è fallita nel modello prima della consegna di una risposta; l'importo bloccato è stato restituitoSì. Reinvia con un nuovo Idempotency-Key
40004400Il modello funziona, ma non con queste impostazioniNo. Cambia l'impostazione indicata in msg

50301 significa che il modello non poteva accettare l'attività al momento dell'invio: non è elencato, non ha prezzi attivi o non ha capacità di generazione disponibile adesso. Problemi temporanei di capacità possono risolversi con un numero limitato di tentativi. Un modello non disponibile o privo di prezzo attivo non viene risolto da richieste ripetute. Aggiorna il catalogo e ottieni la conferma dell'utente prima di cambiare modello.

40004 e 50301 sono intenzionalmente diversi. 50301 significa che il modello non può accettare alcuna attività adesso: aspettare può aiutare. 40004 significa che funziona ma non può gestire questa combinazione di impostazioni; msg indica l'impostazione fra parentesi, per esempio This model cannot serve this parameter combination right now (…). Aspettare non aiuta; cambiare un valore sì. Nessuno dei due crea un'attività o blocca denaro.

Gli errori di generazione dopo l'accettazione compaiono come state: failed

Un errore di generazione dopo che createTask ha restituito un ID compare in recordInfo come state: "failed" con errorCode / errorMessage; l'importo bloccato viene normalmente liberato per intero, tranne alcuni rifiuti di contenuto esplicitamente dichiarati come addebitabili nella pagina del modello. La stessa situazione di 40004, rilevata soltanto dopo l'accettazione, compare come errorCode: "unsupported_combination". Non aggiungere rami per codici applicativi sincroni assenti da questa pagina.

Codici di errore delle attività

Un errore dopo l'accettazione finisce in errorCode su recordInfo. L'insieme è chiuso: la diagnostica interna non appare mai qui, quindi gestisci qualsiasi valore fuori da questo elenco come upstream_failed.

errorCodeSignificatoRiprovare?
invalid_requestI parametri non sono stati accettatiNo. Correggili e invia di nuovo
unsupported_combinationQuesta combinazione non è supportata adessoNo. Scegli impostazioni diverse
content_rejectedIl modello scelto ha rifiutato questo contenuto. SpicyAPI stesso non esamina le richieste: consulta Responsabilità sui contenuti e sull'utilizzoNo. Cambia il prompt oppure scegli un altro modello
rate_limitedIl servizio di generazione era occupatoSì. Attendi progressivamente e riprova
upstream_unavailableLa generazione non è temporaneamente disponibileSì. Riprova fra poco
invalid_assetUna risorsa di riferimento non è più disponibileNon con un nuovo tentativo della stessa attività. retry riutilizza l'input originale, quindi carica nuovamente la risorsa e crea una nuova attività con il nuovo URI
generation_failedLa generazione è partita e non ha prodotto nullaForse. Gli stessi input di solito falliscono allo stesso modo
timeoutL'attività ha superato la scadenza senza terminareForse. Prova durata più breve o risoluzione inferiore
upstream_failedCategoria generale per il restoSì. Attendi progressivamente e riprova

Le attività fallite o scadute normalmente liberano l'intero importo bloccato. Alcuni rifiuti di contenuto vengono addebitati al preventivo accettato soltanto quando la pagina del modello dichiara esplicitamente questa eccezione. Controlla settled e cost per l'addebito finale. errorMessage è per le persone: descrive il motivo in prosa, può riportare il motivo del modello in inglese (vedi sopra) e la formulazione cambia: basa la logica soltanto su errorCode. Qui «riprovare» significa inviare una nuova attività, addebitata nuovamente se riesce.

Quali errori costano denaro

Lo stato dell'attività e quello della connessione sono fatti diversi.

Creazioni esplicitamente rifiutate, attività scadute e registrazioni storiche annullate non comportano addebiti di generazione. Le attività fallite sono normalmente gratuite, salvo alcuni rifiuti di contenuto esplicitamente dichiarati come addebitabili nella pagina del modello. Un timeout o una disconnessione del client non è un errore dell'attività. Le attività accettate non possono essere annullate: recupera l'originale ed esamina stato finale e regolamento dell'addebito. Uno stream di testo disconnesso può essere addebitato quando è già stato registrato un utilizzo valido.

SituazioneAddebito?
L'API rifiuta esplicitamente createTask senza creare un'attività (per esempio 40201, 40202, 40004 o 50301)No. Creazione e blocco fondi condividono una transazione; un errore del gateway o una risposta mancante non dimostrano un rifiuto
L'invio ha un timeout, una scheda si chiude o la connessione si interrompeAncora sconosciuto. Recupera l'accettazione usando la chiave di idempotenza originale; non considerarlo un nuovo tentativo gratuito
createTask restituisce 202, l'attività termina poi failed / expiredNormalmente nessun addebito. Alcuni esiti failed / content_rejected vengono addebitati al preventivo accettato quando la pagina del modello lo dichiara esplicitamente. Leggi settled e cost
Un'attività storica è canceledNo. È uno stato di compatibilità, non un'azione attualmente disponibile
createTask restituisce 202, l'attività è succeededSì, in base all'utilizzo effettivo ma entro il blocco accettato. I fondi inutilizzati vengono liberati; non viene riscosso in seguito un importo oltre il blocco
Qualsiasi errore da recordInfo, chat/credit o common/*No. Questi endpoint non sono mai fatturati
Limite di frequenza (429) oppure deduplicazione con chiave di idempotenzaNo. Nel primo caso non è mai avvenuta l'accettazione; nel secondo viene restituita l'attività originale, addebitata una sola volta

Il regolamento o lo sblocco si completano automaticamente, anche dopo l'interruzione di un processo. Gli errori ordinari liberano l'intero importo; alcuni rifiuti di contenuto esplicitamente dichiarati nella pagina del modello vengono addebitati al preventivo accettato. Controlla regolamento dell'attività e saldo per verificare l'esito: consulta Fatturazione.

Quando vale la pena riprovare un 409

CasoRiprovare?
Raro conflitto simultaneo su createTask con la stessa chiave e payload invariatoSì. Attendi qualche centinaio di millisecondi e riprova con la stessa chiave. Consulta Idempotenza
La stessa chiave è già stata usata per un altro payload o un'altra chiave APINo. Fermati e correggi la titolarità della chiave; riprovare non cambia il conflitto
Immagini inline o conferma del caricamento ancora in elaborazione (Retry-After può essere presente)Sì. Attendi Retry-After o un paio di secondi e reinvia con lo stesso Idempotency-Key
Prezzi o configurazione dell'account cambiati mentre la richiesta era in corsoSì. Ottieni un nuovo preventivo se li usi, poi reinvia
Artefatto ancora in copiaSì. Attendi qualche secondo e chiedi di nuovo il link di download
jobs/retry su un'attività non finita o con regolamento non ancora conclusoSì, più tardi. Attendi che la sorgente sia in stato finale e settled sia true, poi riprova
jobs/retry su un'attività riuscita oppure con input già rimosso per conservazioneNo. Crea una nuova attività con createTask
URL di caricamento o file caricato scadutoNo. Carica nuovamente il file

Scrivere la logica dei nuovi tentativi

Non riprovare alla cieca

400, 401, 40004, 40201 ed errori analoghi di input o autorizzazione danno la stessa risposta qualunque sia il numero di tentativi. Ogni tentativo indicato come «No» sopra è uno spreco e consuma il tuo limite di frequenza.

Attesa esponenziale con una variazione casuale, così una serie di errori non torna a inviare richieste nello stesso istante:

JavaScript
// 409 is operation-specific and must be handled at the call site.
const RETRYABLE = new Set([429, 500, 503, 50301]);

async function callWithRetry(path, init, { attempts = 4 } = {}) {
  for (let i = 0; i < attempts; i++) {
    const res = await fetch(`https://api.spicyapi.ai/api/v1${path}`, init);

    // A page from the edge network is not our JSON envelope
    if (!res.headers.get('content-type')?.includes('json')) {
      const text = await res.text();
      throw new Error(`HTTP ${res.status}, not JSON: ${text.slice(0, 200)}`);
    }
    const body = await res.json();

    if (body.code === 200) return body.data;
    if (!RETRYABLE.has(body.code)) {
      throw new Error(`${body.code} ${body.msg} (request_id=${body.request_id})`);
    }

    // When the server says how long to wait, trust it
    const retryAfter = Number(res.headers.get('retry-after'));
    const backoff = Number.isFinite(retryAfter) && retryAfter > 0
      ? retryAfter * 1000
      : 2 ** i * 1000 + Math.random() * 500;

    await new Promise((r) => setTimeout(r, backoff));
  }
  throw new Error('out of retries');
}
Python
import random, time, requests

# 409 is operation-specific and must be handled at the call site.
RETRYABLE = {429, 500, 503, 50301}
BASE = "https://api.spicyapi.ai/api/v1"


def call_with_retry(method: str, path: str, attempts: int = 4, **kwargs) -> dict:
    for i in range(attempts):
        res = requests.request(method, f"{BASE}{path}", **kwargs)

        # A page from the edge network is not our JSON envelope
        try:
            body = res.json()
        except ValueError:
            raise RuntimeError(f"HTTP {res.status_code}, not JSON: {res.text[:200]}")

        if body["code"] == 200:
            return body["data"]
        if body["code"] not in RETRYABLE:
            raise RuntimeError(f'{body["code"]} {body["msg"]} (request_id={body.get("request_id")})')

        # When the server says how long to wait, trust it
        retry_after = res.headers.get("retry-after")
        backoff = float(retry_after) if retry_after else 2**i + random.random() * 0.5
        time.sleep(backoff)

    raise RuntimeError("out of retries")
Go
package main

import (
	"encoding/json"
	"fmt"
	"io"
	"math/rand"
	"net/http"
	"strconv"
	"time"
)

// retryable lists the codes worth another attempt. Anything else returns
// the same answer however many times you ask — and each ask eats your own
// rate limit.
// Whether the generation itself failed is read from recordInfo's state.
var retryable = map[int]bool{429: true, 500: true, 503: true, 50301: true}

type envelope struct {
	Code      int             `json:"code"`
	Msg       string          `json:"msg"`
	Data      json.RawMessage `json:"data"`
	RequestID string          `json:"request_id"`
}

func callWithRetry(newReq func() (*http.Request, error), attempts int, out any) error {
	for i := 0; i < attempts; i++ {
		req, err := newReq()
		if err != nil {
			return err
		}
		res, err := http.DefaultClient.Do(req)
		if err != nil {
			// Transport-level failure: the request may well have arrived.
			// On createTask, send the same Idempotency-Key or this retry
			// turns into a second generation and a second charge.
			time.Sleep(backoff(i, ""))
			continue
		}

		var env envelope
		decErr := json.NewDecoder(res.Body).Decode(&env)
		io.Copy(io.Discard, res.Body)
		res.Body.Close()
		if decErr != nil {
			// Not our JSON envelope, for example a page from the edge network.
			return fmt.Errorf("HTTP %d, response is not JSON: %w", res.StatusCode, decErr)
		}

		if env.Code == 200 {
			return json.Unmarshal(env.Data, out)
		}
		if !retryable[env.Code] {
			return fmt.Errorf("%d %s (request_id=%s)", env.Code, env.Msg, env.RequestID)
		}
		// When the server says how long to wait, trust it
		time.Sleep(backoff(i, res.Header.Get("Retry-After")))
	}
	return fmt.Errorf("out of retries")
}

// backoff trusts Retry-After when present, otherwise backs off
// exponentially with jitter so a batch of failures does not come back
// as one thundering herd.
func backoff(attempt int, retryAfter string) time.Duration {
	if s, err := strconv.Atoi(retryAfter); err == nil && s > 0 {
		return time.Duration(s) * time.Second
	}
	base := time.Duration(1<<attempt) * time.Second
	return base + time.Duration(rand.Intn(500))*time.Millisecond
}

Quando riprovi createTask, invia sempre lo stesso Idempotency-Key; altrimenti «era riuscito, abbiamo soltanto perso la risposta» diventa una generazione duplicata e un addebito duplicato.

Diagnostica

Quando un errore sembra incomprensibile, percorri questo elenco:

  1. request_id: forniscicelo e possiamo trovare la richiesta esatta.
  2. code: le tabelle sopra.
  3. Log nella console: incolla l'ID dell'attività o request_id nella ricerca della pagina Log per aprire l'attività: stato, costo, latenza, chiave che l'ha inviata, codice di errore e motivo. Parametri e risultati vengono mostrati soltanto mentre sono conservati e, in un team, soltanto ai ruoli autorizzati a leggere i contenuti.

Il msg per 500 è sempre un generico «servizio temporaneamente non disponibile» senza dettagli interni: nella risposta non compaiono mai struttura interna, nomi di componenti o stack trace. Questi possono essere rintracciati soltanto attraverso request_id.

Per contattarci, apri una conversazione sotto Assistenza nella console oppure scrivi a support@spicyapi.ai. Includi request_id o ID dell'attività, ora (UTC) e code. Non inviare mai una chiave API.

In questa pagina