pallets/flask · error · TypeError

The view function for

Error message

The view function for {request.endpoint!r} did not return a valid response. The function either returned None or ended without a return statement.

What it means

Raised when a view function's effective body (rv) is None after tuple unpacking. A Flask view must produce a response body; None means the function returned nothing or returned None explicitly, so Flask cannot build a response.

Solutions

  1. Add an explicit return to every branch, e.g. return '' or return jsonify(...).
  2. Return a proper Response or status tuple like ('', 204).
  3. Run a linter/type checker (mypy, pyright) to flag functions missing returns.

Example fix

// before
@app.route('/x')
def x():
    log_event()  # no return -> raises
// after
@app.route('/x')
def x():
    log_event()
    return '', 204
Defensive patterns

Strategy: validation

Validate before calling

def require_body(rv):
    if rv is None:
        raise TypeError('View must return a non-None body')
    return rv

Type guard

def has_body(rv) -> bool:
    return rv is not None

Try / catch

try:
    return view()
except TypeError as e:
    if 'did not return a valid response' in str(e):
        return '', 204
    raise

Prevention

When it happens

Trigger: A view with no return statement, a view that returns None, or a view that returns a tuple whose first element is None.

Common situations: Forgot a return statement after a refactor; early return with no value (return) in a conditional branch; view that logs and implicitly falls through.

Related errors


AI-assisted analysis of pallets/flask@3596b1ab61 (2026-08-11). Data as JSON: /api/errors/86949444004012ae. Report an issue: GitHub.

Appendix: source

Thrown at src/flask/app.py:1310

            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
                # special logic
                rv = self.response_class(
                    rv,  # pyright: ignore
                    status=status,
                    headers=headers,  # type: ignore[arg-type]
                )
                status = headers = None
            elif isinstance(rv, (dict, list)):

View on GitHub (pinned to 3596b1ab61)