Un registro aceptó un ID duplicado y el fallo apareció tres pasos después
Dos filas quedaron con el mismo identificador y cada comando respondió éxito. El error salió tres pasos después, como un rechazo correcto sobre el papel y falso sobre la realidad.

Un registro de trabajo en vuelo daba un identificador a cada unidad, y todo lo que venía detrás colgaba de ese identificador. Dos filas acabaron con el mismo ID, creadas con un día de diferencia. Nada se quejó. El síntoma apareció tres pasos más tarde, cuando una operación que no tenía nada que ver fue rechazada por solaparse con una unidad que, según el registro, ya había aterrizado.
Los dos pasos que dijeron que sí
Al dar de alta la segunda fila, el comando de alta respondió éxito. Al procesarla, el comando de aterrizaje también respondió éxito: se llevó por delante la fila antigua y dejó la nueva en vuelo. Los dos mensajes eran ciertos y ninguno apuntaba al problema. El rechazo llegó mucho después, de la mano de una comprobación de solapamiento sobre otra unidad distinta. Esa negativa era correcta respecto al contenido del registro y falsa respecto al mundo: lo que decía aterrizado no lo estaba, y lo que había aterrizado era otra fila con el mismo nombre.
La disciplina para emitir identificadores se había respetado. Un solo escritor, sin colisiones por descuido. El duplicado vino de una fila en cola que nadie tenía en la cabeza cuando se abrió la segunda. El registro aceptó algo que su propia invariante documentada prohibía, porque esa invariante vivía en la prosa y en la costumbre, y el código que escribe filas nunca la consultó. Una restricción documentada y no comprobada la sostiene la memoria, y la memoria aguanta un tiempo. Ese es justo el problema: la primera violación es silenciosa, y cuando algo visible pasa, el rastro ya está frío.
La corrección
Dos rechazos, en los dos puntos donde el registro puede ver la violación. Al dar de alta, negarse si el ID ya existe en cualquier estado: abierto, aterrizado o abandonado. Al aterrizar, negarse cuando dos filas comparten el identificador. Ambas son la misma condición. Lo interesante era dónde ponerlas: el alta es el primer momento en que la violación existe, así que ahí el mensaje es útil, y el aterrizaje queda como red de seguridad para filas creadas antes de que el chequeo existiera.
El "en cualquier estado" hace trabajo real. Excluir las filas aterrizadas o abandonadas parece más limpio, porque están acabadas y no se ve por qué iban a bloquear un ID nuevo. También reintroduce exactamente el mismo fallo: aquí la colisión fue contra una fila que solo estaba en cola, y la resolución recorre todas las filas sin mirar el estado. Filtrar por estado antes de comprobar unicidad no comprueba unicidad.
La lección general: la unicidad se hace valer en la inserción, no en el uso. En el uso ya hay más de un candidato y cualquier criterio para elegir uno (el primero, el más nuevo, el tocado más recientemente) es una apuesta que acertará lo bastante a menudo como para esconder el problema. Y si algo resuelve un ID sin filtrar por estado, y siempre hay algo que lo hace, una fila terminada sigue siendo una colisión viva.
El modo de fallo no es un crash. Es un rechazo de aspecto correcto, en otro sitio, por una razón que es verdadera sobre tus registros y falsa sobre tu proyecto.

