Conversation
DOIOrTitleBasedProvider.query means to degrade to None when a provider errors ("we're suppressing this error to not fail on 403 or 500 errors from providers"), but since the move to httpx it catches httpx.RequestError, which does not cover httpx.HTTPStatusError. aiohttp.ClientError, which it replaced, did. A status that is not retried, like Semantic Scholar's 429 for anonymous traffic, therefore escapes the metadata lookup, and during indexing process_file marks the paper as failed and re-raises it, ending the whole index build. Catching httpx.HTTPError, the common base of both, restores the old behaviour
Assisted-by: Claude Code:claude-opus-5
Contributor
There was a problem hiding this comment.
🟢 Approval recommended
The focused change is regression-tested and has no unresolved blocking issues.
Pull request overview
Updates metadata providers to gracefully handle HTTP errors such as 429 responses without aborting indexing.
Changes:
- Catch
httpx.HTTPErrorduring provider queries. - Add regression tests for Crossref and Semantic Scholar 429 responses.
File summaries
| File | Summary |
|---|---|
tests/test_clients.py |
Tests graceful handling of 429 responses. |
src/paperqa/clients/client_models.py |
Suppresses HTTP transport and status errors during metadata lookup. |
Review details
- Files reviewed: 2/2 changed files
- Comments generated: 0
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
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.
DOIOrTitleBasedProvider.queryis meant to degrade toNonewhen a metadata provider errors, as its comment says: "we're suppressing this error to not fail on 403 or 500 errors from providers". Since #1062 moved the clients from aiohttp to httpx it catcheshttpx.RequestError, which covers transport failures but nothttpx.HTTPStatusError;aiohttp.ClientError, which it replaced, covered both. So any status thatis_retryabledoes not retry escapes the lookup. The one that bites in practice is Semantic Scholar's 429 for anonymous trafficDuring indexing that escalates:
process_filemarks the paper as failed, re-raises because the error is not aValueError, and the task group ends the wholeget_directory_indexrun. Withparsing.use_doc_detailson (the default) and noSEMANTIC_SCHOLAR_API_KEY, one 429 on one paper is enough:Reproduced deterministically on v2026.08.12 with
pqa indexover one PDF, after patchingpaperqa.clients.semantic_scholar.SEMANTIC_SCHOLAR_BASE_URLto a local server that answers every request with 429; the same code is onmainThis PR catches
httpx.HTTPError, the common base ofRequestErrorandHTTPStatusError, which restores what the aiohttp version did. The paper then gets a plainDocand a warning, the same as when a provider is unreachableTests
test_client_http_status_error, parametrized overCrossrefProviderandSemanticScholarProvider, has the HTTP client answer 429 and expectsqueryto returnNone. It fails onmainwithhttpx.HTTPStatusErrorand passes with the changetests/test_clients.pypasses in full with--record-mode=none(51 tests);mypyand the pinnedruff-checkandblackare clean on both filesA related behaviour, left alone here: once a file is marked
ERROR,SearchIndex.filechecktreats it as indexed, so later builds into the same index skip it without a log line, even when the failure was transient like this one. Happy to open an issue for that if it is worth discussing