pallets/flask · error · TypeError
The view function did not return a valid response tuple. The
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 in `Flask.make_response()` (src/flask/app.py:1299) when a view returns a tuple whose length is not 2 or 3. Flask unpacks tuple return values as (body, status, headers), (body, status), or (body, headers); a 1-tuple, empty tuple, or 4+-tuple has no defined interpretation, so dispatch fails rather than guessing.
Source
Thrown at src/flask/app.py:1299
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 6a2f545bfd)
Solutions
- Check the return statement for a stray trailing comma and remove it.
- Conform to one of the accepted shapes: `return body`, `return body, status`, `return body, headers`, or `return body, status, headers`.
- If returning sequence data as JSON, return a list/dict (Flask JSON-serializes those) or wrap with `jsonify(...)` — never a bare tuple.
- For anything more complex (cookies, streaming), build a Response with `make_response()` and set attributes on it.
Example fix
# before return jsonify(data), # trailing comma makes a 1-tuple # after return jsonify(data)
Defensive patterns
Strategy: type-guard
Type guard
def is_valid_response_tuple(rv) -> bool:
return isinstance(rv, tuple) and len(rv) in (2, 3) Prevention
- Return tuples only in the shapes (body, status), (body, headers), or (body, status, headers)
- Never return a 1-tuple or 4+-tuple; unpack accidental trailing commas (`return body,`)
- Use `flask.make_response()` explicitly when composing complex responses
- Add a test-client test per endpoint asserting status and body
When it happens
Trigger: `return (data,)` (trailing comma), `return ()`, `return body, status, headers, extra`, or returning the result of a function that yields a wrongly-sized tuple. Note a 2-tuple whose second element is not a Headers/dict/tuple/list is treated as (body, status), not rejected here.
Common situations: Accidental trailing comma after a return value; returning `tuple(some_list)` of query results directly instead of jsonify-ing it (a tuple of rows becomes a 'response tuple'); spreading extra values like `return body, 200, headers, cookie`; refactoring that removed one element of a 3-tuple incorrectly.
Related errors
- The view function for {request.endpoint!r} did not return a
- {e} The view function did not return a valid response. The r
- The view function did not return a valid response. The retur
- Working outside of application context. Attempted to use fu
- Working outside of request context. Attempted to use functi
AI-assisted analysis of pallets/flask@6a2f545bfd (2026-07-31).
Data as JSON: /data/errors/4e0d106f51ad9e95.json.
Report an issue: GitHub ↗.