Conversation
Constructing a typed error set the response status there and then. The status of an error belongs to the error reaching the client, not to the moment the object was made, and the two are not the same: an error that is caught is never answered with. A route that catches a ForbiddenError in order to report something, and then succeeds, answered its complete result under a 403. The composer does exactly that to report a field an object will not take any more. plone.jsonapi.core sets the status from the exception when it renders the envelope, which is the moment the error becomes the answer. The legacy setStatus alias still applies one by hand, and now records it on the error too, so the two cannot disagree.
This was referenced Oct 5, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
APIError.__init__sets the response status as a side effect of constructing the error. The status of an error belongs to the error reaching the client, not to the moment the object was made, and the two are not the same: an error that is caught is never answered with.Current behavior before PR
A route that catches a
ForbiddenErrorin order to report something, and then succeeds, answers its complete result under a 403:That is senaite.composer reporting a field an object will not take any more, which is worth reporting and not worth failing on. Any caller that catches a typed error hits this.
Desired behavior after PR is merged
The error carries its status, and
bika.lims.jsonapi.handle_errorssets it when it renders the envelope, which is the moment the error becomes the answer. Nothing else changes: the same status reaches the same responses for every error that is actually raised to the client.setStatus()stays for callers outside this package that apply one by hand, and now records the status on the error as well, so the two cannot disagree.Why the CI is red
Against
senaite.core2.x as it is today, the error handler never sets a status. The constructor side effect this PR removes is what stood in for it, so with it gone and nothing yet put in its place, every error answers 200.The doctests say it plainly:
Expected a 401, got a 200 with a failure envelope. That is the bug this pair of changes is about, seen from the other side.
senaite/senaite.core#3038 teaches the handler to set the status from the exception. With that in, these doctests pass unchanged.
Verification
senaite.corewhose handler sets the status:bin/test-senaite -s senaite.jsonapiis green, 38 tests including every doctest above.setStatusstill applies and now records.