Skip to content

Narrow exception scope lending#12220

Closed
Arnav-0206 wants to merge 3 commits intointernetarchive:masterfrom
Arnav-0206:narrow-exception-scope-lending
Closed

Narrow exception scope lending#12220
Arnav-0206 wants to merge 3 commits intointernetarchive:masterfrom
Arnav-0206:narrow-exception-scope-lending

Conversation

@Arnav-0206
Copy link
Copy Markdown

Closes #12217

Refactor

Technical

This PR narrows the exception scope in openlibrary/core/lending.py by replacing broad except Exception blocks with more specific exception types, as indicated by existing TODO comments.

  • Replaced with:

    • httpx.HTTPError for httpx network operations
    • requests.RequestException for requests-based calls
    • JSONDecodeError for JSON parsing
  • Removed corresponding TODO comments where addressed

  • Preserved expected behavior for network/API failures

  • Kept the broad exception in sync_loan() intentionally, as it handles infogami-related errors without a specific exception type

Testing

  • Ran the project locally using Docker
  • Verified no change in expected behavior for network/API calls
  • Confirmed unrelated exceptions (e.g., TypeError, KeyError) are no longer silently swallowed

Screenshot

Not applicable (no UI changes)

Stakeholders

Copilot AI review requested due to automatic review settings March 29, 2026 15:59
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Refactors openlibrary/core/lending.py to narrow previously broad exception handling around IA/network calls and JSON parsing, aligning behavior with the intent of existing TODOs while avoiding swallowing unrelated errors.

Changes:

  • Replaced several except Exception blocks with targeted network/JSON exceptions (httpx.HTTPError, requests.RequestException, JSONDecodeError).
  • Removed corresponding TODO comments where the exception scope was narrowed.
  • Kept the broad exception in sync_loan() intentionally.

Comment on lines +673 to 674
except (httpx.HTTPError, JSONDecodeError):
logger.exception(f"get_loan({identifier}) 2 of 2")
Copy link

Copilot AI Mar 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as the prior except: if .json() fails for the IA loan lookup, httpx will commonly raise ValueError/json.JSONDecodeError rather than simplejson.errors.JSONDecodeError. Consider extending the caught decode exception types here too so malformed JSON is handled consistently.

Copilot uses AI. Check for mistakes.
Comment on lines +321 to 323
except (httpx.HTTPError, JSONDecodeError):
logger.exception(f"get_available({url})")
return {'error': 'request_timeout'}
Copy link

Copilot AI Mar 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

httpx.Response.json() raises ValueError/json.JSONDecodeError on invalid JSON (see similar handling in openlibrary/core/ia.py). Catching simplejson.errors.JSONDecodeError here likely won’t catch decode failures from httpx, changing behavior from returning {'error': 'request_timeout'} to raising. Consider catching json.JSONDecodeError (and/or ValueError) alongside httpx.HTTPError (or switch to requests/simplejson decoding consistently).

Copilot uses AI. Check for mistakes.
Comment on lines +493 to 495
except (httpx.HTTPError, JSONDecodeError) as e:
logger.exception("lending.get_availability", extra={'ids': ids})
availabilities.update(
Copy link

Copilot AI Mar 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

resp.json() here is from httpx (via ia.session), which typically raises ValueError/json.JSONDecodeError on invalid JSON rather than simplejson.errors.JSONDecodeError. This means JSON decode failures may now escape this handler and bubble up. Consider catching json.JSONDecodeError (and/or ValueError) alongside httpx.HTTPError.

Copilot uses AI. Check for mistakes.
Comment on lines +631 to 633
except (httpx.HTTPError, JSONDecodeError):
logger.exception(f"is_loaned_out_on_ia({identifier})")
return None
Copy link

Copilot AI Mar 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ia.session is an httpx.Client, so .json() decode failures will usually raise ValueError/json.JSONDecodeError rather than simplejson.errors.JSONDecodeError. With the narrowed except, invalid JSON may now raise instead of returning None. Consider catching json.JSONDecodeError (and/or ValueError) here too.

Copilot uses AI. Check for mistakes.
Comment on lines +668 to 670
except (httpx.HTTPError, JSONDecodeError):
logger.exception(f"get_loan({identifier}) 1 of 2")

Copy link

Copilot AI Mar 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both calls to _get_ia_loan ultimately rely on httpx responses; decode failures from .json() typically raise ValueError/json.JSONDecodeError, not simplejson.errors.JSONDecodeError. These handlers may stop catching malformed JSON and allow exceptions to bubble up. Consider catching json.JSONDecodeError (and/or ValueError) in these except clauses as well.

Copilot uses AI. Check for mistakes.
@mekarpeles mekarpeles added the Priority: 4 An issue, but should be worked on when no other pressing work can be done. [managed] label Mar 31, 2026
@mekarpeles
Copy link
Copy Markdown
Member

It would have been nice if discussed before PR opened; the blanket exceptions weren't great but the question becomes whether these exceptions will catch all the errors we care about (did we trade a working catch-all for something too specific)

@Arnav-0206
Copy link
Copy Markdown
Author

Thank you @mekarpeles for the feedback!
Sure thing, I will make the PR the way you suggested.

Regarding the question of whether these cover all the error types we really care about:

For the IA_Lending_API._post hook (line 1125): Limited this to requests.RequestException. As this is the universal base class for the requests library, it will safely catch all the timeouts, connection errors, and HTTP errors without hiding native Python exceptions (like KeyError or TypeError).

For the ia.session.get().json() calls: Pinpointed httpx.HTTPError (which is effectively the base class for all network/timeout/status exceptions in httpx) along with a fallback to JSONDecodeError in case the IA endpoint sends us a corrupted response body.

For the database sync_loan call (line 815): Specifically kept except Exception as a decent catch all since infogami throws very broad errors on document update conflicts.

I have one question though: if we want to do an exhaustive test to cover all the possible cases, does that mean writing a test for every single exception and error type from libs we use? Will that be overkill?

I am totally determined to make this absolutely right. If you think httpx.HTTPError is still too risky/specific for the lending checks, I would be very glad to extend it to any base exception you consider safest for production. Tell me what you want me to do and I will change the PR accordingly :D

@github-actions github-actions Bot added the Needs: Response Issues which require feedback from lead label Mar 31, 2026
@RayBB
Copy link
Copy Markdown
Collaborator

RayBB commented Mar 31, 2026

Closing as this does not follow our contributing guidelines (https://docs.openlibrary.org/developers/CONTRIBUTING.html)

In this case, you're opening a PR before you're assigned to an issue. It's important that you're assigned to ensure it's an issue that staff are prioritizing to review and work on. If no issue exists you can create it.

Thanks for understanding and we welcome you to find another issue follow the process linked above.

@RayBB RayBB closed this Mar 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Needs: Response Issues which require feedback from lead Priority: 4 An issue, but should be worked on when no other pressing work can be done. [managed]

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Refactor: Narrow exception scope in lending.py

5 participants