urllib3/urllib3 · error · ValueError
Unable to determine whether fp is closed.
Error message
Unable to determine whether fp is closed.
What it means
Raised as ValueError from is_fp_closed() when the supplied file-like object has none of: isclosed() method, closed attribute, or fp attribute. urllib3 uses these three duck-typed signals (in that order) to decide if a response stream is exhausted; if none exists it cannot make a determination and refuses to guess.
Source
Thrown at src/urllib3/util/response.py:37
# GH Issue #928
return obj.isclosed() # type: ignore[no-any-return, attr-defined]
except AttributeError:
pass
try:
# Check via the official file-like-object way.
return obj.closed # type: ignore[no-any-return, attr-defined]
except AttributeError:
pass
try:
# Check if the object is a container for another file-like object that
# gets released on exhaustion (e.g. HTTPResponse).
return obj.fp is None # type: ignore[attr-defined]
except AttributeError:
pass
raise ValueError("Unable to determine whether fp is closed.")
def assert_header_parsing(headers: httplib.HTTPMessage) -> None:
"""
Asserts whether all headers have been successfully parsed.
Extracts encountered errors from the result of parsing headers.
Only works on Python 3.
:param http.client.HTTPMessage headers: Headers to verify.
:raises urllib3.exceptions.HeaderParsingError:
If parsing errors are found.
"""
# This will fail silently if we pass in the wrong kind of parameter.
# To make debugging easier add an explicit check.
if not isinstance(headers, httplib.HTTPMessage):View on GitHub (pinned to c8d039c1b7)
Solutions
- Ensure custom file-like bodies inherit from io.IOBase (which provides .closed) or expose at least one of isclosed/closed/fp.
- Avoid calling urllib3.util.response.is_fp_closed() on objects that aren't response-like; use resp.closed on HTTPResponse directly.
- For mocks, set the closed attribute explicitly (mock.closed = True/False).
Example fix
// before
class MyBody:
def read(self, n=-1): return b''
is_fp_closed(MyBody()) # raises ValueError
// after
import io
class MyBody(io.IOBase):
def read(self, n=-1): return b''
# .closed is now provided by io.IOBase Defensive patterns
Strategy: type-guard
Validate before calling
def fp_closed(fp):
for attr in ('isclosed', 'closed'):
if hasattr(fp, attr):
return getattr(fp, attr)
if hasattr(fp, 'fp'):
return fp.fp is None
raise ValueError('object is not a recognizable file-like response') Type guard
def looks_like_response_fp(obj) -> bool:
return any(hasattr(obj, a) for a in ('isclosed', 'closed', 'fp')) Try / catch
try:
closed = is_fp_closed(obj)
except ValueError:
closed = True # unknown shape; assume closed to stop iterating Prevention
- Inherit custom file-like bodies from io.IOBase
- Don't pass arbitrary objects to is_fp_closed
When it happens
Trigger: Passing an arbitrary object to is_fp_closed() that doesn't resemble a file-like/HTTPResponse; using a custom body wrapper that omits all three signals; contrib backends whose body object lacks the expected interface.
Common situations: Test doubles/mocks that implement read() but nothing else; third-party transports that wrap responses in plain objects; legacy Python 2 shims that lost the closed attribute.
Related errors
- The file-like object this HTTPResponse is wrapped around has
- body_pos must be of type integer, instead it was {type(body_
- 'body' must be a bytes-like object, file-like object, or ite
- expected httplib.Message, got {type(headers)}.
- Invalid Retry-After header: {retry_after}
AI-assisted analysis of urllib3/urllib3@c8d039c1b7 (2026-08-04).
Data as JSON: /data/errors/8c3f6a35f48fa754.json.
Report an issue: GitHub.