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

  1. Return exactly a 2- or 3-tuple in the documented shape.
  2. For header-only responses return (body, headers) where headers is a dict/Headers.
  3. 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

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


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 any

View on GitHub (pinned to 3596b1ab61)