pallets/flask · error · TypeError

The view function did not return a valid response. The…

Error message

The view function did not return a valid response. The return type must be a string, dict, list, tuple with headers or status, Response instance, or WSGI callable, but it was a {type(rv).__name__}.

What it means

Raised when a view returns a value that matches none of the accepted types (str, bytes, bytearray, dict, list, iterator, BaseResponse, callable). This is the catch-all TypeError for an unhandleable return value.

Solutions

  1. Wrap the value in jsonify(...) for dict/list-like data, or str(...) for primitives.
  2. For a status-only response, return a tuple: ('', 204).
  3. Register a custom response class or FORCED response type if a domain object must be returned.

Example fix

// before
@app.route('/count')
def count():
    return 42  # raises
// after
@app.route('/count')
def count():
    return jsonify({'count': 42})
Defensive patterns

Strategy: validation

Validate before calling

from collections.abc import Iterator
ACCEPTED = (str, bytes, bytearray, dict, list, Iterator)
def is_acceptable_return(rv) -> bool:
    return isinstance(rv, ACCEPTED) or callable(rv)

Type guard

def is_acceptable_return(rv) -> bool:
    return isinstance(rv, (str, bytes, bytearray, dict, list)) or callable(rv)

Try / catch

try:
    return rv
except TypeError as e:
    if 'return type' in str(e):
        return jsonify({'error': 'serialization failed'}), 500
    raise

Prevention

When it happens

Trigger: A view returns an int, float, bool, None-already-handled, custom object, tuple-of-wrong-shape-already-unpacked object, etc. Example: return 42.

Common situations: Returning a bare integer status code expecting it to set status; returning a model/dataclass object that isn't serializable; returning True/False from a check.

Related errors


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

Appendix: source

Thrown at src/flask/app.py:1347

                rv = self.json.response(rv)
            elif isinstance(rv, BaseResponse) or callable(rv):
                # evaluate a WSGI callable, or coerce a different response
                # class to the correct type
                try:
                    rv = self.response_class.force_type(
                        rv,  # type: ignore[arg-type]
                        request.environ,
                    )
                except TypeError as e:
                    raise TypeError(
                        f"{e}\nThe view function did not return a valid"
                        " response. The return type must be a string,"
                        " dict, list, tuple with headers or status,"
                        " Response instance, or WSGI callable, but it"
                        f" was a {type(rv).__name__}."
                    ).with_traceback(sys.exc_info()[2]) from None
            else:
                raise TypeError(
                    "The view function did not return a valid"
                    " response. The return type must be a string,"
                    " dict, list, tuple with headers or status,"
                    " Response instance, or WSGI callable, but it was a"
                    f" {type(rv).__name__}."
                )

        rv = t.cast(Response, rv)
        # prefer the status if it was provided
        if status is not None:
            if isinstance(status, (str, bytes, bytearray)):
                rv.status = status
            else:
                rv.status_code = status

        # extend existing headers with provided headers
        if headers:
            rv.headers.update(headers)

View on GitHub (pinned to 3596b1ab61)