pallets/flask · error · TypeError
The view function did not return a valid response tuple…
Error message
The view function did not return a valid response tuple. The tuple must have the form (body, status, headers), (body, status), or (body, headers).
What it means
Raised during response coercion when a view returns a tuple whose length is not 2 or 3. Flask only unpacks tuples of the form (body, status, headers), (body, status), or (body, headers); any other length is ambiguous and rejected as a TypeError.
Solutions
- Return exactly a 2- or 3-tuple in the documented shape.
- For header-only responses return (body, headers) where headers is a dict/Headers.
- Construct a Response object directly to avoid tuple ambiguity.
Example fix
// before return (body, status, headers, 'extra') # raises // after return (body, status, headers)
Defensive patterns
Strategy: validation
Validate before calling
def is_valid_response_tuple(rv):
return isinstance(rv, tuple) and len(rv) in (2, 3) Type guard
def is_valid_response_tuple(rv) -> bool:
return isinstance(rv, tuple) and len(rv) in (2, 3) Try / catch
try:
return view_return_value
except TypeError:
# log and return a safe Response
pass Prevention
- Always return 2- or 3-tuples in the documented shape.
- Prefer Response objects for non-trivial returns to avoid ambiguity.
- Add unit tests asserting views return valid shapes.
When it happens
Trigger: A view returns a 1-element or 4+-element tuple, e.g. return (body,) or return (body, status, headers, extra).
Common situations: Accidentally returning a nested tuple; returning a tuple variable that grew an extra element via dynamic code; misordered return statement.
Related errors
- \nThe view function did not return a valid response. The…
- The view function did not return a valid response. The…
- The view function for
- Allowed methods must be a list of strings, for example…
- is an instance, not a class. Handlers can only be…
AI-assisted analysis of pallets/flask@3596b1ab61 (2026-08-11).
Data as JSON: /api/errors/4e0d106f51ad9e95.
Report an issue: GitHub.
Appendix: source
Thrown at src/flask/app.py:1302
status: int | None = None
headers: HeadersValue | None = None
# unpack tuple returns
if isinstance(rv, tuple):
len_rv = len(rv)
# a 3-tuple is unpacked directly
if len_rv == 3:
rv, status, headers = rv # type: ignore[misc]
# decide if a 2-tuple has status or headers
elif len_rv == 2:
if isinstance(rv[1], (Headers, dict, tuple, list)):
rv, headers = rv # pyright: ignore
else:
rv, status = rv # type: ignore[assignment,misc]
# other sized tuples are not allowed
else:
raise TypeError(
"The view function did not return a valid response tuple."
" The tuple must have the form (body, status, headers),"
" (body, status), or (body, headers)."
)
# the body must not be None
if rv is None:
raise TypeError(
f"The view function for {request.endpoint!r} did not"
" return a valid response. The function either returned"
" None or ended without a return statement."
)
# make sure the body is an instance of the response class
if not isinstance(rv, self.response_class):
if isinstance(rv, (str, bytes, bytearray)) or isinstance(rv, cabc.Iterator):
# let the response class set the status and headers instead of
# waiting to do it manually, so that the class can handle anyView on GitHub (pinned to 3596b1ab61)