psf/requests · error · ValueError
You can only send PreparedRequests.
Error message
You can only send PreparedRequests.
What it means
Raised as a plain `ValueError` from `Session.send()` in `src/requests/sessions.py:768`. `Session.send()` transmits an already-prepared request; it guards against being handed a raw `requests.Request` (which lacks a URL-encoded body, merged headers, and cookies) because the two types are easy to confuse. The public API path is `Session.request()`/`requests.get()` etc., which prepare the request for you.
Source
Thrown at src/requests/sessions.py:768
return self.request("DELETE", url, **kwargs)
def send(self, request: PreparedRequest, **kwargs: Any) -> Response:
"""Send a given PreparedRequest.
:rtype: requests.Response
"""
# Set defaults that the hooks can utilize to ensure they always have
# the correct parameters to reproduce the previous request.
kwargs.setdefault("stream", self.stream)
kwargs.setdefault("verify", self.verify)
kwargs.setdefault("cert", self.cert)
if "proxies" not in kwargs:
kwargs["proxies"] = resolve_proxies(request, self.proxies, self.trust_env)
# It's possible that users might accidentally send a Request object.
# Guard against that specific failure case.
if isinstance(request, Request):
raise ValueError("You can only send PreparedRequests.")
assert _is_prepared(request)
# Set up variables needed for resolve_redirects and dispatching of hooks
allow_redirects = kwargs.pop("allow_redirects", True)
stream = kwargs.get("stream")
hooks = request.hooks
# Get the appropriate adapter to use
adapter = self.get_adapter(url=request.url)
# Start time (approximately) of the request
start = preferred_clock()
# Send the request
r = adapter.send(request, **kwargs)
# Total elapsed time of the request (approximately)View on GitHub ↗ (pinned to 414f0513c3)
Solutions
- Prepare the request first: `prepped = session.prepare_request(req); resp = session.send(prepped)` — prefer `session.prepare_request` over `req.prepare()` so session cookies/headers merge.
- If you don't need the prepared-request pattern, call `session.request('GET', url, ...)` or `requests.get(url)` instead of `send()`.
Example fix
# before
req = requests.Request('GET', url, headers=h)
resp = session.send(req)
# after
req = requests.Request('GET', url, headers=h)
prepped = session.prepare_request(req)
resp = session.send(prepped) Defensive patterns
Strategy: type-guard
Validate before calling
from requests import Request, PreparedRequest
req = Request('GET', url).prepare() # prepare() before session.send()
assert isinstance(req, PreparedRequest) Type guard
from requests import PreparedRequest
def is_prepared(req):
return isinstance(req, PreparedRequest) Try / catch
try:
resp = session.send(req)
except ValueError as e:
if 'PreparedRequests' in str(e):
resp = session.send(session.prepare_request(req))
else:
raise Prevention
- Call session.prepare_request(request) (or request.prepare()) before session.send() — send() only accepts PreparedRequest
- Prefer the high-level session.get/post/request methods unless you specifically need the prepare/send split
- Type-annotate helper functions that pass requests around (req: PreparedRequest) so mypy catches the mixup
When it happens
Trigger: Calling `session.send(req)` where `req = requests.Request('GET', url)` was never passed through `session.prepare_request(req)` or `req.prepare()`. Common in code using the prepared-request pattern for header inspection, custom auth signing, or test frameworks that build Request objects manually.
Common situations: Following the prepared-request docs but skipping the `prepare_request()` step; refactors that changed a variable from the prepared to the raw request; middleware/instrumentation that intercepts and re-sends requests; mocking layers that hand back a Request instead of a PreparedRequest.
Related errors
- Exceeded {self.max_redirects} redirects.
- Files must be provided.
- Data must not be a string.
- Unsupported event specified, with event name "{event}"
- Invalid URL {url!r}: No scheme supplied. Perhaps you meant h
AI-assisted analysis of psf/requests@414f0513c3 (2026-07-31).
Data as JSON: /data/errors/f86ed6905d7e9161.json.
Report an issue: GitHub ↗.