Ir al contenido

Errores y resultados de escaneo

Esta es la página de consulta para gestionar fallos: el cuerpo de error que devuelve cada endpoint, los códigos para los que merece la pena crear ramas y, lo más importante, las tablas canónicas de resultados de los escaneos. «Sin coincidencia» es un resultado, no un fallo de transporte, y aun así llega con un estado de error HTTP. El código que trata cada respuesta que no sea 2xx como un error notificará interrupciones que nunca ocurrieron.

Los esquemas de cada endpoint están en la referencia de la API. Los propios flujos están en Identificadores y en la guía de inicio rápido.

Cada solicitud fallida devuelve el mismo objeto 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 } }
}
CampoTipoSignificado
codestringCódigo estable legible por máquina. Cree ramas basándose en este campo. Nunca analice message.
messagestringTexto legible por personas, localizado según Dust-Ctx-Locale. La redacción cambia; los códigos no.
statusnumberRefleja el estado HTTP.
detailobject, opcionalContexto específico del error: detalles de validación, el recibo de escaneo, la clasificación del resultado e identificadores de conflictos.

status y el estado HTTP siempre coinciden, por lo que puede crear ramas basándose en cualquiera de ellos. Cada respuesta, tanto correcta como fallida, también incluye una cabecera x-request-id. Regístrela; el equipo de soporte la utiliza para encontrar su solicitud exacta.

CódigoEstadoCuándo
INVALID_REQUEST400Cuerpo, consulta o cabecera con formato incorrecto. Los detalles de validación están en detail.
INVALID_DATA400La solicitud se analizó, pero los valores no pueden utilizarse (por ejemplo, una carga útil de identificación que el servicio de escaneo se negó a leer).
UNAUTHORIZED401Falta el token al portador, ha caducado o no es válido.
FORBIDDEN403La autenticación es válida, pero este contexto no puede realizar esa acción.
ATTRIBUTION_REQUIRED403La política de atribución de la cuenta de servicio es required y la escritura no incluía ningún Dust-Ctx-Declared-Actor.
ORG_ID_REQUIRED / TEAM_ID_REQUIRED400Falta una cabecera de contexto en un endpoint con ámbito definido.
NOT_FOUND / NO_DATA_FOUND404No hay ningún registro de ese tipo visible en este contexto.
RATE_LIMITED429Espere un tiempo creciente y vuelva a intentarlo.
THREAD_DATA_CONFLICT409Conflicto de concurrencia optimista: su expectedUpdatedAt está desactualizado. Vuelva a leer los datos y a aplicar los cambios.
COMPOSITE_TAG_CONFLICT409Conflicto de Etiqueta: un DUST ya está en otra Etiqueta o vinculado en otro lugar, una posición de la bobina está ocupada o se está quitando el último Identificador de una Etiqueta. detail.reason indica cuál es el caso.
UNKNOWN_ERROR / SERVICE_ERROR500Fallo del servidor. Vuelva a intentarlo con espera incremental; incluya el identificador de la solicitud si el problema persiste.
CódigoEstadoSignificado
IDENTIFIER_NOT_FOUND404Ausencia de coincidencia definitiva: todas las particiones consultadas respondieron y no hubo ninguna coincidencia.
IDENTIFIER_NOT_BOUND404El Identificador existe, pero no está vinculado a ninguna Ficha (solo se puede llegar a este resultado mediante una identificación por tagId).
IDENTIFIER_ALREADY_BOUND409Se rechazó la vinculación: ese Identificador (o su Etiqueta) ya está en otra Ficha. detail.compositeTagId identifica la Etiqueta.
IDENTIFIER_VERIFY_FAILED500La verificación de un único Identificador no produjo una coincidencia, o el Identificador indicado no está vinculado a esa Ficha.
SCAN_AMBIGUOUS_MATCH409Dos o más DUST inscritos distintos coincidieron y ambos están vinculados dentro del ámbito consultado. No se puede volver a intentar con la misma captura.
SCAN_SEARCH_INCOMPLETE503Algunas particiones respondieron «sin coincidencia», pero no se pudieron consultar otras. Esto no es una ausencia de coincidencia: vuelva a intentarlo.
SCAN_LOW_KEYPOINTS / SCAN_NO_KEYPOINTS400Se rechazó la propia captura: no contiene suficientes detalles utilizables. Vuelva a escanear; no reintente con la misma imagen.
SCAN_IDENTICAL_SCAN400La imagen enviada contiene exactamente los mismos bytes que una captura anterior. Tome una nueva.
SCAN_EXTRACTION_FAILURE500La extracción falló en una imagen que, por lo demás, se había aceptado.
SCAN_ROUTING_UNAVAILABLE503La operación no está disponible para esta organización (por ejemplo, un módulo que no está habilitado) o su ruta está fuera de servicio.
SCAN_BACKEND_UNAVAILABLE503El backend de escaneo no está disponible temporalmente. La captura es válida: vuelva a intentarlo, no vuelva a escanear.

POST /api/v1/tags/identify tiene ocho resultados. Tres devuelven 200; el resto llegan con estados de error y aun así son respuestas. Esta tabla es la fuente única tanto para integraciones humanas como para integraciones de agentes: la misma tabla aparece en las skills dice-api-integration y dust-go-connect-integration.

ResultadoHTTPCuerpoQué significaQué hacer
Ficha identificada200{ type: "identified", identified: { tag, thread, … }, scan? }Coincidió exactamente un Identificador vinculado.Abra la Ficha. scan.dustId es el DUST resuelto.
Varios candidatos200{ type: "matches", matches: [ … ], scan? }Coincidió más de un Identificador vinculado o es necesario desambiguar la coincidencia.Muestre los candidatos y vuelva a identificar por tagId (tagType: "ANY"). scan.dustId es null; cada candidato incluye el suyo.
Etiqueta sin vincular200{ type: "label", label: { label, tags }, scan? }El escaneo se resolvió como miembro de una Etiqueta del inventario de su Equipo que aún no está vinculada a ninguna Ficha.Ofrezca vincular la Etiqueta. No es una ausencia de coincidencia.
Sin coincidencia404code: "IDENTIFIER_NOT_FOUND", detail.outcome: "no_match"Todas las particiones consultadas respondieron y no hubo ninguna coincidencia. detail.teamsSearched indica el ámbito.Muestre «no encontrado». No notifique un fallo del servicio. El recibo está en detail.scan.
Búsqueda incompleta503code: "SCAN_SEARCH_INCOMPLETE", detail.outcome: "search_incomplete"Algunas particiones respondieron «sin coincidencia» y no se pudo acceder a otras. detail.teamsSearched, detail.teamsUnreachable, detail.orgsUnreachable.Vuelva a intentarlo. Nunca muestre este resultado como «no encontrado»: es muy posible que el artículo esté inscrito.
Coincidencia ambigua409code: "SCAN_AMBIGUOUS_MATCH", detail.outcome: "ambiguous", detail.candidates, detail.boundCandidates, detail.attemptsDos o más DUST vinculados coincidieron con un alto grado de confianza. La plataforma buscó dos veces con la misma captura antes de devolver este resultado.Muestre el problema junto con scan.scanId y póngase en contacto con DUST Identity. Una nueva captura del mismo artículo no lo resolverá.
Captura rechazada400code: "SCAN_LOW_KEYPOINTS" / "SCAN_NO_KEYPOINTS" / "SCAN_IDENTICAL_SCAN", detail.outcome: "quality_reject"No se pudo utilizar la imagen. El rechazo por calidad tiene prioridad sobre cualquier otro resultado de las particiones.Pida al operador que vuelva a escanear. La imagen se conserva como Escaneo rechazado; detail.scan.fingerprintId es null.
Identificador sin vincular404code: "IDENTIFIER_NOT_BOUND"Solo se produce en una identificación por tagId (tagType: "ANY"): el Identificador existe, pero no tiene ninguna Ficha.Ofrezca vincularlo.

detail.outcome es la clasificación utilizada por el servidor y es estable: no_match, search_incomplete, ambiguous, quality_reject. Cree ramas basándose primero en code y lea detail.outcome cuando necesite una distinción más precisa.

Creación de ramas según un resultado de identificación

Sección titulada «Creación de ramas según un resultado de identificación»
// 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 se comporta de forma diferente según cuántos Identificadores candidatos envíe en tags, porque un candidato plantea una pregunta de sí o no, mientras que varios candidatos constituyen una búsqueda:

Longitud de tagsCoincidenciaSin coincidencia
Exactamente uno200 con { tag, scan? }IDENTIFIER_VERIFY_FAILED (500), recibo en detail.scan
Dos o más200 con { success: true, verifiedTag, attemptedCount, failedCount, scan? }200 con { success: false, attemptedCount, failedCount, error, scan? }

Por tanto, una verificación en bloque que falla es una llamada HTTP correcta con success: false. Lea siempre success cuando envíe más de un candidato y nunca deduzca la autenticidad únicamente a partir de response.ok.

tags es obligatorio en todas las verificaciones. Es un array de objetos, no de identificadores:

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

Los recibos de escaneo sobreviven a los fallos

Sección titulada «Los recibos de escaneo sobreviven a los fallos»

Cada operación que envía una imagen —extracción, vinculación, identificación, verificación o análisis de alteraciones— devuelve un recibo de escaneo que indica qué se almacenó:

{ "scanId": "…", "fingerprintId": "…", "dustId": "…" }
  • Cuando la operación se completa correctamente, aparece en el nivel superior como scan.
  • Cuando el resultado se almacena, pero es negativo, aparece en detail.scan: una verificación sin coincidencia, una identificación sin coincidencia, una vinculación rechazada por duplicidad o un rechazo por calidad (en cuyo caso fingerprintId es null, porque no se extrajo nada utilizable).

Capture scanId en ambos casos. Es la identidad estable de la captura a través de las migraciones de algoritmos y es lo que el equipo de soporte necesita para consultar la imagen asociada a un resultado controvertido. Solo una imagen que la plataforma no pudo decodificar en absoluto no almacena nada; en ese caso no hay recibo.

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

dustId es lo que la plataforma le devolvió, nunca una coincidencia interna sin procesar: es null en una discrepancia, una ausencia de coincidencia, una extracción, un análisis de alteraciones y una identificación que devolvió varios candidatos.

Los tokens al portador tienen una vida corta y no existe ningún token de actualización: debe volver a intercambiar la credencial. Un token caducado genera un 401 UNAUTHORIZED normal, indistinguible de uno revocado, por lo que debe gestionar ambos del mismo modo:

  1. Renueve el token de forma proactiva. GET /api/auth/token devuelve expiresIn (segundos) y expiresAt (ISO 8601) siempre que el token incluya una declaración de caducidad. Vuelva a intercambiarlo con cierto margen (60 segundos resulta adecuado); nunca codifique de forma fija su duración.
  2. Reintente una vez tras un 401. Tanto el desfase del reloj como una revocación durante la vigencia del token producen este resultado. Una renovación seguida de un único reintento es suficiente; un bucle no lo es.
  3. Emita un token por solicitud desde una caché, no una sola vez al iniciar el proceso, para que un trabajo que dure más que un token no falle a mitad de la ejecución.

La implementación completa está en Autenticación → Caducidad y renovación de tokens.

Situación¿Reintentar la misma solicitud?Notas
401 UNAUTHORIZEDSí, una vez, después de volver a intercambiar la credencialMás de un intento significa que la propia credencial es incorrecta.
429 RATE_LIMITEDSí, con espera incremental
503 SCAN_SEARCH_INCOMPLETE / SCAN_BACKEND_UNAVAILABLESí; la captura es válidaNo haga que el operador vuelva a escanear.
503 SCAN_ROUTING_UNAVAILABLENoLa operación no está disponible para esta organización; consulte a DUST Identity.
400 SCAN_LOW_KEYPOINTS / SCAN_NO_KEYPOINTS / SCAN_IDENTICAL_SCANNo; vuelva a escanearLos mismos bytes volverán a rechazarse.
404 IDENTIFIER_NOT_FOUNDNoEs una respuesta.
409 SCAN_AMBIGUOUS_MATCHNoYa se volvió a intentar en el servidor; detail.attempts lo indica.
409 THREAD_DATA_CONFLICTVuelva a leer, aplique de nuevo los cambios y, después, escribaNo reintente a ciegas: sobrescribiría los cambios de otra persona.
5xx UNKNOWN_ERRORSí, con espera incremental, para lecturas idempotentesPara escrituras, compruebe si la escritura se realizó antes de reintentarlo.