Specify 7.12.0.7 (docker, stock image), MariaDB 10.x.
A collections manager returning items on an old loan got four 500s in a row on PUT /api/specify/loan/1241/. He confirms what the audit log shows: the loan had an unassociated item (no preparation) with Quantity = NULL, every save that touched only preparation-linked items went through, the saves that resolved the unassociated item failed with the generic error dialog, and as soon as he typed a quantity into that item the next save resolved it and closed the loan. The gunicorn log shows [WARNING] specifyweb.backend.interactions.cog_preps:324 Parsing of interaction preparations failed immediately before each Internal Server Error: /api/specify/loan/1241/. No traceback was logged (see the aside below), so I don't have the exception name.
To reproduce:
- Open a loan and add an unassociated item. Leave Quantity empty. Save. This succeeds; all eight such rows in our databases were created this way in Specify 7 between 2022 and July 2026.
- Later, set a returned/resolved quantity on that item and save.
- Expected: a validation message saying the item needs a quantity (refusing is, of course, fine). Actual: HTTP 500, the exception only in the response body, and the user sees a generic error dialog with nothing to act on. He found the fix by guessing.
Either accepting the empty quantity at step 1 should be disallowed, or the resolve at step 2 should fail with a message. Workaround: enter a Quantity on the item first.
Aside: GeneralMiddleware puts the exception class, message and traceback only into the 500 response body. Django then logs a single Internal Server Error: <path> line with no exception attached, so nothing about the failure reaches the server logs. A logger.exception(...) in process_exception would give admins the traceback without changing what the browser gets.
Specify 7.12.0.7 (docker, stock image), MariaDB 10.x.
A collections manager returning items on an old loan got four 500s in a row on
PUT /api/specify/loan/1241/. He confirms what the audit log shows: the loan had an unassociated item (no preparation) withQuantity = NULL, every save that touched only preparation-linked items went through, the saves that resolved the unassociated item failed with the generic error dialog, and as soon as he typed a quantity into that item the next save resolved it and closed the loan. The gunicorn log shows[WARNING] specifyweb.backend.interactions.cog_preps:324 Parsing of interaction preparations failedimmediately before eachInternal Server Error: /api/specify/loan/1241/. No traceback was logged (see the aside below), so I don't have the exception name.To reproduce:
Either accepting the empty quantity at step 1 should be disallowed, or the resolve at step 2 should fail with a message. Workaround: enter a Quantity on the item first.
Aside:
GeneralMiddlewareputs the exception class, message and traceback only into the 500 response body. Django then logs a singleInternal Server Error: <path>line with no exception attached, so nothing about the failure reaches the server logs. Alogger.exception(...)inprocess_exceptionwould give admins the traceback without changing what the browser gets.