Skip to content

fix: don't cache Response objects with error status codes - #575

Open
RitiGrover wants to merge 1 commit into
long2ice:mainfrom
RitiGrover:fix/dont-cache-error-responses
Open

RitiGrover wants to merge 1 commit into
long2ice:mainfrom
RitiGrover:fix/dont-cache-error-responses

Conversation

@RitiGrover

Copy link
Copy Markdown

Description

@cache only checks whether the wrapped function's result is a Response to know how to encode it - it never looks at the status code. If an endpoint returns an error Response directly (a common alternative to raising HTTPException, e.g. via custom response shortcuts), that response gets cached exactly like a success response. On the next cache hit, the wrapper returns the cached body but the response object it hands back defaults to 200, so the client sees a 200 with the error's JSON body instead of the original error status.

Motivation and Context

Fixes #554. Nothing in the cache entry records the original status code, so there's no way to "correctly" cache an error response without a bigger change to what gets stored. The minimal, safe fix is to just not cache it - matching how requests that are already _uncacheable() are handled (skip caching, return the result as-is).

How Has This Been Tested?

  • Added cache_error_response_obj to examples/in_memory/main.py, returning a JSONResponse with status_code=404 and an incrementing counter in the body.
  • Added test_cache_error_response_obj_is_not_cached, asserting the status code stays 404 and the counter increments across two calls (proving it isn't cached).
  • Confirmed red -> green: without the fix, the second call comes back as 200 with the cached (stale) body; with the fix, both calls correctly return 404 with fresh bodies.
  • Ran the full tests/test_decorator.py suite: my new test passes, and the two pre-existing failures (test_datetime - timing-sensitive, test_non_get - unrelated PUT on a GET-only route) are present identically on main before my change, so nothing here is a regression.
  • ruff check clean on all changed files (the formatting suggestions ruff format makes on decorator.py/main.py/test_decorator.py are all pre-existing, on lines I didn't touch, so I left them alone rather than reformatting unrelated code).

@cache only checks whether the result is a Response to encode it, it
never looks at the status code. An endpoint that returns an error
Response directly (rather than raising HTTPException) gets that
response cached as-is; a later cache hit replays the cached body but
the wrapper always returns a plain 200, so the client sees a 200 with
error content instead of the original status.

Skip caching entirely for a Response with status_code >= 400 and
return it directly, matching how uncacheable requests are already
handled.

Fixes long2ice#554

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

@cache decorator assumes that all error responses are indicated by an HTTPException raise

1 participant