What happened?
When two users assign the same kit to custody at the same time, one request fails with a generic 500 server error instead of a clear "kit already in custody" conflict message. The kit is still assigned only once (data integrity holds), but the loser of the race sees an unhandled server error.
Steps to reproduce:
- Have a kit that is available (not in custody).
- Fire two "assign custody" requests for that kit at nearly the same moment (two operators, or a double-click / retry).
- One request wins. The other hits the unique constraint on
KitCustody and returns a 500.
In bulkAssignKitCustody (apps/webapp/app/modules/kit/service.server.ts), the assignment runs tx.kitCustody.createMany(...) at line 2416. When a concurrent assignment already created the row, Prisma throws a P2002 unique-constraint violation. That falls through to the outer catch at line 2540, which rethrows a ShelfError with the generic message "Something went wrong while bulk checking out kits" and no explicit status or shouldBeCaptured: false. The result is a captured 500 and an error-report entry for what is really an expected concurrency conflict.
What is the expected behaviour?
A concurrent double-assignment should return a handled 4xx (409 conflict or a 400 "kit unavailable") with a clear message, and should not be captured as an error. The transaction rollback and single-assignment behaviour already work correctly; only the error classification needs fixing. The smallest fix is to recognize the P2002 on the KitCustody uniqueness constraint in this catch and throw a handled conflict error with shouldBeCaptured: false, mirroring how other expected-conflict paths in this file are already handled (for example around lines 2354-2395).
Version: Self-hosted (reproduced against main @ 7e139f5)
Found while running Ito (AI code review, free for open source) against recently merged PRs — full analysis: https://app.ito.ai/share/a4681a63-43f5-47c3-8a22-c664ed8acce7.
What happened?
When two users assign the same kit to custody at the same time, one request fails with a generic 500 server error instead of a clear "kit already in custody" conflict message. The kit is still assigned only once (data integrity holds), but the loser of the race sees an unhandled server error.
Steps to reproduce:
KitCustodyand returns a 500.In
bulkAssignKitCustody(apps/webapp/app/modules/kit/service.server.ts), the assignment runstx.kitCustody.createMany(...)at line 2416. When a concurrent assignment already created the row, Prisma throws aP2002unique-constraint violation. That falls through to the outer catch at line 2540, which rethrows aShelfErrorwith the generic message "Something went wrong while bulk checking out kits" and no explicitstatusorshouldBeCaptured: false. The result is a captured 500 and an error-report entry for what is really an expected concurrency conflict.What is the expected behaviour?
A concurrent double-assignment should return a handled 4xx (409 conflict or a 400 "kit unavailable") with a clear message, and should not be captured as an error. The transaction rollback and single-assignment behaviour already work correctly; only the error classification needs fixing. The smallest fix is to recognize the
P2002on theKitCustodyuniqueness constraint in this catch and throw a handled conflict error withshouldBeCaptured: false, mirroring how other expected-conflict paths in this file are already handled (for example around lines 2354-2395).Version: Self-hosted (reproduced against
main@7e139f5)Found while running Ito (AI code review, free for open source) against recently merged PRs — full analysis: https://app.ito.ai/share/a4681a63-43f5-47c3-8a22-c664ed8acce7.