Adopt an exception-handling standard that distinguishes critical control-flow from non-critical side effects, while enforcing safe, observable exception patterns.
1) Use framework-correct error propagation
raise HTTPException(...) (never return HTTPException(...)).2) Prefer specific exceptions where the library provides/uses distinct types
BotoCoreError and ClientError; OpenAI/LiteLLM retries: include timeout/connection types you want retried).3) For non-critical side effects, fail open—but document and log
except, keep it broad only in these fail-open zones, and add a comment explaining why narrowing would be unsafe.4) Re-raise correctly to preserve traceback
raise over raise e when rethrowing.5) Don’t use bare/empty catches
except: and avoid empty except blocks.6) Make errors observable and stable
Example (safe fail-open cleanup + correct re-raise + framework raise)
from fastapi import HTTPException
from pr_agent.log import get_logger
def handler():
# framework boundary: must raise
raise HTTPException(status_code=503, detail="Service unavailable")
def best_effort_cleanup(git_provider, comment_url, cleanup_fn):
# non-critical side effect: fail-open
try:
cleanup_fn()
except Exception as cleanup_error:
get_logger().warning(
f"Cleanup failed for {comment_url}; continuing. error={cleanup_error}"
)
def retryable_call():
try:
do_request()
except KnownTransientError:
raise # allow retry framework to handle
except Exception:
get_logger().exception("Unexpected error; not retrying")
raise
When to broaden exceptions: only for explicitly fail-open side effects (dedup loads, cleanup, best-effort provider operations). Otherwise, narrow to specific, meaningful exceptions and re-raise properly when you must abort.