Report an AI provider error instead of ending the request with a 500 - #1539
Open
killecaptron wants to merge 1 commit into
Open
Report an AI provider error instead of ending the request with a 500#1539killecaptron wants to merge 1 commit into
killecaptron wants to merge 1 commit into
Conversation
Creating a part from an URL with the AI extractor enabled ends in an Internal Server Error whenever the AI provider rejects the request - an unknown model name, an exhausted quota or an invalid key all look the same from the outside: Symfony\AI\Platform\Exception\BadRequestException: "Provider returned error" at ResultConverter.php line 63 The handling for this is already there, it is just bypassed. callLLM() wraps the invocation in a try/catch which turns any failure into a RuntimeException with a readable message, but the platform hands out a deferred result: invoke() only prepares the request, and it is carried out (and its answer converted) when the result is read. That read sits one line below the catch, so the very error the catch was written for escapes it. Reading the result inside the try fixes that. The search page then reports the failure as it always did, since it already catches RuntimeException, logs it and shows a message. The "create from URL" page needed the same catch: it only handled HttpClient exceptions, so even the wrapped RuntimeException would have ended as a 500 there. It now behaves like the search page - flash message plus a log entry - which is also where a user is most likely to meet a misconfigured AI provider, since that page is the one which uses it. Reporting the failure is only half of it though, because the message can be useless on its own. "Provider returned error" is the wording a gateway like OpenRouter uses when the model provider behind it refused, and the actual reason travels in a field of the response body which the platform's converter drops. The raw response is still available at this point, so the status code and the beginning of the body are added to the message: without them an administrator cannot tell an exhausted quota from a rejected request. The regression tests build a platform whose result fails when it is read, which is how a provider error really arrives, and check that the failure is reported rather than escaping, and that the answer of the provider is part of the message. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #1539 +/- ##
============================================
+ Coverage 62.39% 62.71% +0.32%
- Complexity 9879 9885 +6
============================================
Files 736 736
Lines 31779 31797 +18
============================================
+ Hits 19829 19942 +113
+ Misses 11950 11855 -95 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
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.
Creating a part from an URL with the AI extractor enabled ends in an Internal Server Error whenever the AI
provider rejects the request. An unknown model name, an exhausted quota and a schema the provider does not
accept all look the same from the outside:
The handling for this is already there, it is just bypassed.
AIWebProvider::callLLM()wraps the invocation ina try/catch which turns any failure into a
RuntimeExceptionwith a readable message, but the platform handsout a deferred result:
invoke()only prepares the request, and it is carried out - and its answer converted -when the result is read. That read sits one line below the catch, so the very error the catch was written for
escapes it.
Reading the result inside the try fixes that. The search page then reports the failure as it always did, since
it already catches
RuntimeException, logs it and shows a message.The "create from URL" page needed the same catch: it only handled HttpClient exceptions, so even the wrapped
RuntimeExceptionwould have ended as a 500 there. It now behaves like the search page, with a flash messageand a log entry - and it is the page where a user is most likely to meet a misconfigured AI provider, since it
is the one which uses it.
Reporting the failure is only half of it though, because the message can be useless on its own. "Provider
returned error" is the wording a gateway like OpenRouter uses when the model provider behind it refused, and
the actual reason travels in a field of the response body which the platform's converter drops. The raw
response is still available at this point, so the status code and the beginning of the body are added to the
message.
That last part is not hypothetical. On my instance the AI extractor failed with exactly the unspecific message
above, and the cause was impossible to guess from it. With the response included, the log said:
which turned out to be a bug in Part-DB itself rather than a misconfiguration.
The regression tests build a platform whose result fails when it is read, which is how a provider error really
arrives, and check that the failure is reported rather than escaping, and that the answer of the provider is
part of the message.