Salta ai contenuti

Errori ed esiti delle scansioni

Questa è la pagina di riferimento per la gestione degli errori: il corpo dell’errore restituito da ogni endpoint, i codici in base ai quali vale la pena creare diramazioni e, soprattutto, le tabelle canoniche degli esiti delle scansioni. Una “nessuna corrispondenza” è un risultato, non un errore di trasporto, e viene comunque restituita con uno stato HTTP di errore. Il codice che considera ogni risposta non 2xx un bug segnalerà interruzioni del servizio mai avvenute.

Gli schemi specifici per ciascun endpoint si trovano nel riferimento API. I flussi sono descritti in Identificatori e nella guida introduttiva.

Ogni richiesta non riuscita restituisce lo stesso oggetto JSON:

{
"code": "IDENTIFIER_NOT_FOUND",
"message": "No match in the 3 searched teams",
"status": 404,
"detail": { "outcome": "no_match", "teamsSearched": 3, "scan": { "scanId": "…", "fingerprintId": "…", "dustId": null } }
}
CampoTipoSignificato
codestringCodice stabile leggibile dalla macchina. Crea diramazioni in base a questo campo. Non analizzare mai message.
messagestringTesto leggibile dalle persone, localizzato in base a Dust-Ctx-Locale. La formulazione cambia; i codici no.
statusnumberRispecchia lo stato HTTP.
detailobject, facoltativoContesto specifico dell’errore: dettagli della convalida, ricevuta di scansione, classificazione dell’esito, ID in conflitto.

status e lo stato HTTP coincidono sempre, quindi puoi creare diramazioni in base all’uno o all’altro. Ogni risposta, riuscita o meno, contiene anche un’intestazione x-request-id. Registrala nei log; l’assistenza la utilizza per individuare la tua richiesta esatta.

CodiceStatoQuando
INVALID_REQUEST400Corpo, query o intestazione con formato non valido. I dettagli della convalida si trovano in detail.
INVALID_DATA400La richiesta è stata analizzata, ma i valori non possono essere utilizzati (per esempio, un payload di identificazione che il servizio di scansione non è riuscito a leggere).
UNAUTHORIZED401Token bearer mancante, scaduto o non valido.
FORBIDDEN403L’autenticazione è riuscita, ma questo contesto non può eseguire l’operazione.
ATTRIBUTION_REQUIRED403Il criterio di attribuzione dell’account di servizio è required e la scrittura non includeva alcun Dust-Ctx-Declared-Actor.
ORG_ID_REQUIRED / TEAM_ID_REQUIRED400Manca un’intestazione di contesto in un endpoint con ambito definito.
NOT_FOUND / NO_DATA_FOUND404In questo contesto non è visibile alcun registro corrispondente.
RATE_LIMITED429Attendi con backoff e riprova.
THREAD_DATA_CONFLICT409Conflitto di concorrenza ottimistica: il tuo expectedUpdatedAt è obsoleto. Leggi di nuovo e riapplica le modifiche.
COMPOSITE_TAG_CONFLICT409Conflitto dell’etichetta: un DUST già presente su un’altra etichetta o associato altrove, una posizione della bobina occupata oppure la rimozione dell’ultimo identificatore di un’etichetta. detail.reason indica quale caso si è verificato.
UNKNOWN_ERROR / SERVICE_ERROR500Errore lato server. Riprova con backoff; se persiste, includi l’ID della richiesta.
CodiceStatoSignificato
IDENTIFIER_NOT_FOUND404Mancata corrispondenza definitiva: ogni partizione interrogata ha risposto e non è stata trovata alcuna corrispondenza.
IDENTIFIER_NOT_BOUND404L’identificatore esiste, ma non è associato ad alcuna Scheda (raggiungibile solo tramite un’identificazione per tagId).
IDENTIFIER_ALREADY_BOUND409Associazione rifiutata: quell’identificatore (o la relativa etichetta) è già presente su un’altra Scheda. detail.compositeTagId indica l’etichetta.
IDENTIFIER_VERIFY_FAILED500La verifica del singolo identificatore non ha trovato una corrispondenza oppure l’identificatore specificato non è associato a quella Scheda.
SCAN_AMBIGUOUS_MATCH409Due o più DUST distinti registrati hanno prodotto una corrispondenza e sono entrambi associati nell’ambito cercato. Non è possibile riprovare con la stessa acquisizione.
SCAN_SEARCH_INCOMPLETE503Alcune partizioni hanno risposto “nessuna corrispondenza”, ma non è stato possibile cercare nelle altre. Non si tratta di una mancata corrispondenza: riprova.
SCAN_LOW_KEYPOINTS / SCAN_NO_KEYPOINTS400L’acquisizione stessa è stata rifiutata: contiene troppo pochi dettagli utilizzabili. Esegui una nuova scansione; non riprovare con la stessa immagine.
SCAN_IDENTICAL_SCAN400I byte dell’immagine inviata sono identici a quelli di un’acquisizione precedente. Acquisiscine una nuova.
SCAN_EXTRACTION_FAILURE500L’estrazione non è riuscita su un’immagine che era stata altrimenti accettata.
SCAN_ROUTING_UNAVAILABLE503L’operazione non è disponibile per questa organizzazione (per esempio, un modulo non abilitato) oppure il relativo percorso non è disponibile.
SCAN_BACKEND_UNAVAILABLE503Il backend di scansione è temporaneamente non disponibile. L’acquisizione è valida: riprova, non eseguire una nuova scansione.

POST /api/v1/tags/identify ha otto esiti. Tre restituiscono 200; gli altri vengono restituiti con stati di errore, ma sono comunque risposte. Questa tabella è l’unica fonte di riferimento sia per le integrazioni umane sia per quelle basate su agenti: la stessa tabella compare nelle skill dice-api-integration e dust-go-connect-integration.

EsitoHTTPCorpoSignificatoCosa fare
Scheda identificata200{ type: "identified", identified: { tag, thread, … }, scan? }È stata trovata esattamente una corrispondenza con un identificatore associato.Apri la Scheda. scan.dustId è il DUST risolto.
Più candidati200{ type: "matches", matches: [ … ], scan? }È stata trovata una corrispondenza con più di un identificatore associato oppure la corrispondenza deve essere disambiguata.Mostra i candidati ed esegui nuovamente l’identificazione tramite tagId (tagType: "ANY"). scan.dustId è null; ogni candidato include il proprio valore.
Etichetta non associata200{ type: "label", label: { label, tags }, scan? }La scansione è stata ricondotta a un elemento di un’etichetta nell’inventario del tuo Team che non è ancora associata ad alcuna Scheda.Proponi di associare l’etichetta. Non è una mancata corrispondenza.
Nessuna corrispondenza404code: "IDENTIFIER_NOT_FOUND", detail.outcome: "no_match"Ogni partizione interrogata ha risposto e non è stata trovata alcuna corrispondenza. detail.teamsSearched indica l’ambito.Mostra “non trovato”. Non segnalare un errore del servizio. La ricevuta si trova in detail.scan.
Ricerca incompleta503code: "SCAN_SEARCH_INCOMPLETE", detail.outcome: "search_incomplete"Alcune partizioni hanno risposto “nessuna corrispondenza”, mentre le altre non erano raggiungibili. detail.teamsSearched, detail.teamsUnreachable, detail.orgsUnreachable.Riprova. Non rappresentare mai questo esito come “non trovato”: l’oggetto potrebbe benissimo essere registrato.
Corrispondenza ambigua409code: "SCAN_AMBIGUOUS_MATCH", detail.outcome: "ambiguous", detail.candidates, detail.boundCandidates, detail.attemptsÈ stata trovata con sicurezza una corrispondenza con due o più DUST associati. La piattaforma ha cercato due volte usando la stessa acquisizione prima di restituire questo esito.Mostra l’esito insieme a scan.scanId e contatta DUST Identity. Una nuova acquisizione dello stesso oggetto non risolverà l’ambiguità.
Acquisizione rifiutata400code: "SCAN_LOW_KEYPOINTS" / "SCAN_NO_KEYPOINTS" / "SCAN_IDENTICAL_SCAN", detail.outcome: "quality_reject"Non è stato possibile utilizzare l’immagine. Il rifiuto per motivi di qualità ha la precedenza su qualsiasi altro esito delle partizioni.Chiedi all’operatore di eseguire una nuova scansione. L’immagine viene conservata come scansione rifiutata; detail.scan.fingerprintId è null.
Identificatore non associato404code: "IDENTIFIER_NOT_BOUND"Solo in caso di identificazione tramite tagId (tagType: "ANY"): l’identificatore esiste ma non è associato ad alcuna Scheda.Proponi di associarlo.

detail.outcome è la classificazione utilizzata dal server ed è stabile: no_match, search_incomplete, ambiguous, quality_reject. Crea prima una diramazione in base a code, quindi leggi detail.outcome quando hai bisogno della distinzione più specifica.

Diramazione in base al risultato di un’identificazione

Sezione intitolata “Diramazione in base al risultato di un’identificazione”
// Runs on your SERVER (it holds the bearer token).
const response = await fetch(`${apidUrl}/api/v1/tags/identify`, {
method: "POST",
headers: { Authorization: `Bearer ${token}`, "Dust-Ctx-Org-Id": organizationId },
body: form,
});
const body = await response.json();
if (response.ok) {
switch (body.type) {
case "identified": return { kind: "thread", thread: body.identified.thread };
case "matches": return { kind: "candidates", candidates: body.matches };
case "label": return { kind: "unbound-label", label: body.label };
default: throw new Error(`Unknown identify result type: ${body.type}`);
}
}
switch (body.code) {
case "IDENTIFIER_NOT_FOUND":
// An answer, not an outage.
return { kind: "no-match", scanId: body.detail?.scan?.scanId ?? null };
case "SCAN_SEARCH_INCOMPLETE":
case "SCAN_BACKEND_UNAVAILABLE":
return { kind: "retry", scanId: body.detail?.scan?.scanId ?? null };
case "SCAN_LOW_KEYPOINTS":
case "SCAN_NO_KEYPOINTS":
case "SCAN_IDENTICAL_SCAN":
return { kind: "rescan", scanId: body.detail?.scan?.scanId ?? null };
case "SCAN_AMBIGUOUS_MATCH":
return { kind: "ambiguous", scanId: body.detail?.scan?.scanId ?? null };
default:
throw new Error(`${body.code}: ${body.message}`);
}

POST /api/v1/tags/verify si comporta in modo diverso in base al numero di identificatori candidati inviati in tags, perché un solo candidato rappresenta una domanda sì/no, mentre più candidati costituiscono una ricerca:

Lunghezza di tagsCorrispondenzaNessuna corrispondenza
Esattamente uno200 con { tag, scan? }IDENTIFIER_VERIFY_FAILED (500), ricevuta in detail.scan
Due o più200 con { success: true, verifiedTag, attemptedCount, failedCount, scan? }200 con { success: false, attemptedCount, failedCount, error, scan? }

Pertanto, una verifica in blocco non riuscita è una chiamata HTTP riuscita con success: false. Quando invii più di un candidato, leggi sempre success e non dedurre mai l’autenticità dal solo valore di response.ok.

tags è obbligatorio in ogni verifica. È un array di oggetti, non di ID:

[{ "tagId": "8f2b…", "tagType": "DUST" }]

Le ricevute di scansione vengono conservate anche in caso di errore

Sezione intitolata “Le ricevute di scansione vengono conservate anche in caso di errore”

Ogni operazione che invia un’immagine — estrazione, associazione, identificazione, verifica, analisi delle alterazioni — restituisce una ricevuta di scansione che indica ciò che è stato archiviato:

{ "scanId": "…", "fingerprintId": "…", "dustId": "…" }
  • In caso di riuscita, si trova al livello principale come scan.
  • In caso di esito negativo ma archiviato, si trova in detail.scan: una mancata corrispondenza della verifica, un’identificazione senza corrispondenze, un’associazione rifiutata perché duplicata e un rifiuto per motivi di qualità (nel quale fingerprintId è null, perché non è stato estratto nulla di utilizzabile).

Acquisisci scanId in entrambi i casi. È l’identità stabile dell’acquisizione durante le migrazioni degli algoritmi ed è ciò che serve all’assistenza per esaminare l’immagine alla base di un risultato contestato. Soltanto un’immagine che la piattaforma non è riuscita affatto a decodificare non archivia nulla; in quel caso non è presente alcuna ricevuta.

const receipt = response.ok ? body.scan : body.detail?.scan;
if (receipt) await recordScan(receipt.scanId, receipt.fingerprintId, receipt.dustId);

dustId rappresenta ciò che la piattaforma ha restituito all’utente, mai una corrispondenza interna non elaborata: è null in caso di mancata corrispondenza, nessuna corrispondenza, estrazione, analisi delle alterazioni e identificazione che ha restituito più candidati.

I token bearer hanno una durata breve e non esiste un token di aggiornamento: devi effettuare nuovamente lo scambio delle credenziali. Un token scaduto produce un normale 401 UNAUTHORIZED, indistinguibile da quello di un token revocato, quindi gestisci entrambi allo stesso modo:

  1. Rinnova in modo proattivo. GET /api/auth/token restituisce expiresIn (secondi) e expiresAt (ISO 8601) ogni volta che il token include un’indicazione di scadenza. Effettua nuovamente lo scambio con un certo margine (60 secondi sono sufficienti); non codificare mai una durata fissa.
  2. Riprova una volta in caso di 401. Sia la differenza tra gli orologi sia una revoca durante il periodo di validità producono questo esito. Un rinnovo seguito da un solo nuovo tentativo è sufficiente; un ciclo non lo è.
  3. Genera un token per ogni richiesta utilizzando una cache, non una sola volta all’avvio del processo, affinché un processo che dura più a lungo di un token non si interrompa a metà.

L’implementazione completa si trova in Autenticazione → Scadenza e rinnovo dei token.

SituazioneRiprovare la stessa richiesta?Note
401 UNAUTHORIZEDSì, una volta, dopo aver effettuato nuovamente lo scambio delle credenzialiPiù di un tentativo indica che la credenziale stessa è errata.
429 RATE_LIMITEDSì, con backoff
503 SCAN_SEARCH_INCOMPLETE / SCAN_BACKEND_UNAVAILABLESì: l’acquisizione è validaNon chiedere all’operatore di eseguire una nuova scansione.
503 SCAN_ROUTING_UNAVAILABLENoL’operazione non è disponibile per questa organizzazione; contatta DUST Identity.
400 SCAN_LOW_KEYPOINTS / SCAN_NO_KEYPOINTS / SCAN_IDENTICAL_SCANNo: esegui invece una nuova scansioneGli stessi byte verranno nuovamente rifiutati.
404 IDENTIFIER_NOT_FOUNDNoÈ una risposta.
409 SCAN_AMBIGUOUS_MATCHNoIl server ha già effettuato un nuovo tentativo; detail.attempts lo indica.
409 THREAD_DATA_CONFLICTLeggi di nuovo, riapplica le modifiche, quindi scriviNon riprovare alla cieca: sovrascriveresti le modifiche di qualcun altro.
5xx UNKNOWN_ERRORSì, con backoff, per le letture idempotentiPer le scritture, verifica che siano state applicate prima di riprovare.