The Fix
Upgrade to version 0.25.0 or later.
Based on closed Kludex/starlette issue #2032 · PR/commit linked
@@ -293,7 +293,7 @@ def test_file_response_with_inline_disposition(tmpdir, test_client_factory):
def test_set_cookie(test_client_factory, monkeypatch):
# Mock time used as a reference for `Expires` by stdlib `SimpleCookie`.
- mocked_now = dt.datetime(2100, 1, 22, 12, 0, 0, tzinfo=dt.timezone.utc)
+ mocked_now = dt.datetime(2037, 1, 22, 12, 0, 0, tzinfo=dt.timezone.utc)
monkeypatch.setattr(time, "time", lambda: mocked_now.timestamp())
[ 40s] ___________________ test_expires_on_set_cookie[asyncio-int] ____________________
[ 40s]
[ 40s] test_client_factory = functools.partial(<class 'starlette.testclient.TestClient'>, backend='asyncio', backend_options={})
[ 40s] monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0xf38e7988>
[ 40s] expires = 10
[ 40s]
[ 40s] @pytest.mark.parametrize(
[ 40s] "expires",
[ 40s] [
[ 40s] pytest.param(
[ 40s] dt.datetime(2100, 1, 22, 12, 0, 10, tzinfo=dt.timezone.utc), id="datetime"
[ 40s] ),
[ 40s] pytest.param("Fri, 22 Jan 2100 12:00:10 GMT", id="str"),
[ 40s] pytest.param(10, id="int"),
[ 40s] ],
[ 40s] )
[ 40s] def test_expires_on_set_cookie(test_client_factory, monkeypatch, expires):
[ 40s] # Mock time used as a reference for `Expires` by stdlib `SimpleCookie`.
[ 40s] mocked_now = dt.datetime(2100, 1, 22, 12, 0, 0, tzinfo=dt.timezone.utc)
[ 40s] monkeypatch.setattr(time, "time", lambda: mocked_now.timestamp())
[ 40s]
[ 40s] async def app(scope, receive, send):
[ 40s] response = Response("Hello, world!", media_type="text/plain")
[ 40s] response.set_cookie("mycookie", "myvalue", expires=expires)
[ 40s] await response(scope, receive, send)
[ 40s]
[ 40s] client = test_client_factory(app)
[ 40s] > response = client.get("/")
[ 40s]
[ 40s] tests/test_responses.py:345:
[ 40s] _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
[ 40s] starlette/testclient.py:492: in get
[ 40s] return super().get(
[ 40s] /usr/lib/python3.8/site-packages/httpx/_client.py:1045: in get
[ 40s] return self.request(
[ 40s] starlette/testclient.py:458: in request
[ 40s] return super().request(
[ 40s] /usr/lib/python3.8/site-packages/httpx/_client.py:821: in request
[ 40s] return self.send(request, auth=auth, follow_redirects=follow_redirects)
[ 40s] /usr/lib/python3.8/site-packages/httpx/_client.py:908: in send
[ 40s] response = self._send_handling_auth(
[ 40s] /usr/lib/python3.8/site-packages/httpx/_client.py:936: in _send_handling_auth
[ 40s] response = self._send_handling_redirects(
[ 40s] /usr/lib/python3.8/site-packages/httpx/_client.py:973: in _send_handling_redirects
[ 40s] response = self._send_single_request(request)
[ 40s] /usr/lib/python3.8/site-packages/httpx/_client.py:1009: in _send_single_request
[ 40s] response = transport.handle_request(request)
[ 40s] starlette/testclient.py:337: in handle_request
[ 40s] raise exc
[ 40s] starlette/testclient.py:334: in handle_request
[ 40s] portal.call(self.app, scope, receive, send)
[ 40s] /usr/lib/python3.8/site-packages/anyio/from_thread.py:283: in call
[ 40s] return cast(T_Retval, self.start_task_soon(func, *args).result())
[ 40s] /usr/lib/python3.8/concurrent/futures/_base.py:437: in result
[ 40s] return self.__get_result()
[ 40s] /usr/lib/python3.8/concurrent/futures/_base.py:389: in __get_result
[ 40s] raise self._exception
[ 40s] /usr/lib/python3.8/site-packages/anyio/from_thread.py:219: in _call_func
[ 40s] retval = await retval
[ 40s] tests/test_responses.py:341: in app
[ 40s] response.set_cookie("mycookie", "myvalue", expires=expires)
[ 40s] starlette/responses.py:140: in set_cookie
[ 40s] cookie_val = cookie.output(header="").strip()
[ 40s] /usr/lib/python3.8/http/cookies.py:502: in output
... (truncated) ...
Re-run the minimal reproduction on your broken version, then apply the fix and re-run.
Option A — Upgrade to fixed release\nUpgrade to version 0.25.0 or later.\nWhen NOT to use: This fix is not applicable if tests are expected to run on platforms supporting dates beyond 2037.\n\n
Why This Fix Works in Production
- Trigger: OverflowError: timestamp out of range for platform time_t
- Mechanism: Tests failed on 32-bit platforms due to date overflow in time_t
- Why the fix works: Fixes tests that were failing on 32-bit architectures by adjusting the expiration date for cookies to a maximum year value of 2037. (first fixed release: 0.25.0).
- If left unfixed, this can cause silent data inconsistencies that propagate (bad cache entries, incorrect downstream decisions).
Why This Breaks in Prod
- Shows up under Python 3.8 in real deployments (not just unit tests).
- Tests failed on 32-bit platforms due to date overflow in time_t
- Surfaces as: OverflowError: timestamp out of range for platform time_t
Proof / Evidence
- GitHub issue: #2032
- Fix PR: https://github.com/encode/starlette/pull/2033
- First fixed release: 0.25.0
- Reproduced locally: No (not executed)
- Last verified: 2026-02-09
- Confidence: 0.85
- Did this fix it?: Yes (upstream fix exists)
- Own content ratio: 0.38
Discussion
High-signal excerpts from the issue thread (symptoms, repros, edge-cases).
“> We just need to change the year? Yeah, that will do it.”
“Would you like to open a PR? Since you can verify the date it will work on those OSs.”
“Closed by https://github.com/encode/starlette/pull/2033”
“Isn't the test wrong anyway? Shouldn't set-cookie be automatically propagated to subsequent requests made by the TestClient? I had to work around this limitation in…”
Failure Signature (Search String)
- OverflowError: timestamp out of range for platform time_t
Error Message
Stack trace
Error Message
-------------
OverflowError: timestamp out of range for platform time_t
Minimal Reproduction
[ 40s] ___________________ test_expires_on_set_cookie[asyncio-int] ____________________
[ 40s]
[ 40s] test_client_factory = functools.partial(<class 'starlette.testclient.TestClient'>, backend='asyncio', backend_options={})
[ 40s] monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0xf38e7988>
[ 40s] expires = 10
[ 40s]
[ 40s] @pytest.mark.parametrize(
[ 40s] "expires",
[ 40s] [
[ 40s] pytest.param(
[ 40s] dt.datetime(2100, 1, 22, 12, 0, 10, tzinfo=dt.timezone.utc), id="datetime"
[ 40s] ),
[ 40s] pytest.param("Fri, 22 Jan 2100 12:00:10 GMT", id="str"),
[ 40s] pytest.param(10, id="int"),
[ 40s] ],
[ 40s] )
[ 40s] def test_expires_on_set_cookie(test_client_factory, monkeypatch, expires):
[ 40s] # Mock time used as a reference for `Expires` by stdlib `SimpleCookie`.
[ 40s] mocked_now = dt.datetime(2100, 1, 22, 12, 0, 0, tzinfo=dt.timezone.utc)
[ 40s] monkeypatch.setattr(time, "time", lambda: mocked_now.timestamp())
[ 40s]
[ 40s] async def app(scope, receive, send):
[ 40s] response = Response("Hello, world!", media_type="text/plain")
[ 40s] response.set_cookie("mycookie", "myvalue", expires=expires)
[ 40s] await response(scope, receive, send)
[ 40s]
[ 40s] client = test_client_factory(app)
[ 40s] > response = client.get("/")
[ 40s]
[ 40s] tests/test_responses.py:345:
[ 40s] _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
[ 40s] starlette/testclient.py:492: in get
[ 40s] return super().get(
[ 40s] /usr/lib/python3.8/site-packages/httpx/_client.py:1045: in get
[ 40s] return self.request(
[ 40s] starlette/testclient.py:458: in request
[ 40s] return super().request(
[ 40s] /usr/lib/python3.8/site-packages/httpx/_client.py:821: in request
[ 40s] return self.send(request, auth=auth, follow_redirects=follow_redirects)
[ 40s] /usr/lib/python3.8/site-packages/httpx/_client.py:908: in send
[ 40s] response = self._send_handling_auth(
[ 40s] /usr/lib/python3.8/site-packages/httpx/_client.py:936: in _send_handling_auth
[ 40s] response = self._send_handling_redirects(
[ 40s] /usr/lib/python3.8/site-packages/httpx/_client.py:973: in _send_handling_redirects
[ 40s] response = self._send_single_request(request)
[ 40s] /usr/lib/python3.8/site-packages/httpx/_client.py:1009: in _send_single_request
[ 40s] response = transport.handle_request(request)
[ 40s] starlette/testclient.py:337: in handle_request
[ 40s] raise exc
[ 40s] starlette/testclient.py:334: in handle_request
[ 40s] portal.call(self.app, scope, receive, send)
[ 40s] /usr/lib/python3.8/site-packages/anyio/from_thread.py:283: in call
[ 40s] return cast(T_Retval, self.start_task_soon(func, *args).result())
[ 40s] /usr/lib/python3.8/concurrent/futures/_base.py:437: in result
[ 40s] return self.__get_result()
[ 40s] /usr/lib/python3.8/concurrent/futures/_base.py:389: in __get_result
[ 40s] raise self._exception
[ 40s] /usr/lib/python3.8/site-packages/anyio/from_thread.py:219: in _call_func
[ 40s] retval = await retval
[ 40s] tests/test_responses.py:341: in app
[ 40s] response.set_cookie("mycookie", "myvalue", expires=expires)
[ 40s] starlette/responses.py:140: in set_cookie
[ 40s] cookie_val = cookie.output(header="").strip()
[ 40s] /usr/lib/python3.8/http/cookies.py:502: in output
... (truncated) ...
Environment
- Python: 3.8
What Broke
Tests for cookie expiration failed, causing CI pipeline failures on 32-bit architectures.
Why It Broke
Tests failed on 32-bit platforms due to date overflow in time_t
Fix Options (Details)
Option A — Upgrade to fixed release Safe default (recommended)
Upgrade to version 0.25.0 or later.
Use when you can deploy the upstream fix. It is usually lower-risk than long-lived workarounds.
Fix reference: https://github.com/encode/starlette/pull/2033
First fixed release: 0.25.0
Last verified: 2026-02-09. Validate in your environment.
When NOT to Use This Fix
- This fix is not applicable if tests are expected to run on platforms supporting dates beyond 2037.
Verify Fix
Re-run the minimal reproduction on your broken version, then apply the fix and re-run.
Did This Fix Work in Your Case?
Quick signal helps us prioritize which fixes to verify and improve.
Prevention
- Capture the exact failing error string in logs and tests so you can reproduce via a minimal script.
- Pin production dependencies and upgrade only with a reproducible test that hits the failing path.
Version Compatibility Table
| Version | Status |
|---|---|
| 0.25.0 | Fixed |
Related Issues
No related fixes found.
Sources
We don’t republish the full GitHub discussion text. Use the links above for context.