pallets/flask · error · TypeError
Allowed methods must be a list of strings, for example: @app
Error message
Allowed methods must be a list of strings, for example: @app.route(..., methods=["POST"])
What it means
A TypeError from `Flask.add_url_rule` (src/flask/sansio/app.py:623-627). The `methods` option must be an iterable of method-name strings; because a plain string is itself an iterable of characters, `methods="POST"` would silently become the methods {'P','O','S','T'} when Flask does `{item.upper() for item in methods}`. Flask special-cases `isinstance(methods, str)` to turn that foot-gun into an immediate, explicit error.
Source
Thrown at src/flask/sansio/app.py:624
self,
rule: str,
endpoint: str | None = None,
view_func: ft.RouteCallable | None = None,
provide_automatic_options: bool | None = None,
**options: t.Any,
) -> None:
if endpoint is None:
endpoint = _endpoint_from_view_func(view_func) # type: ignore
options["endpoint"] = endpoint
methods = options.pop("methods", None)
# if the methods are not given and the view_func object knows its
# methods we can use that instead. If neither exists, we go with
# a tuple of only ``GET`` as default.
if methods is None:
methods = getattr(view_func, "methods", None) or ("GET",)
if isinstance(methods, str):
raise TypeError(
"Allowed methods must be a list of strings, for"
' example: @app.route(..., methods=["POST"])'
)
methods = {item.upper() for item in methods}
# Methods that should always be added
required_methods: set[str] = set(getattr(view_func, "required_methods", ()))
if provide_automatic_options is None:
provide_automatic_options = getattr(
view_func, "provide_automatic_options", None
)
if provide_automatic_options is None:
provide_automatic_options = (
"OPTIONS" not in methods
and self.config["PROVIDE_AUTOMATIC_OPTIONS"]
)View on GitHub ↗ (pinned to 6a2f545bfd)
Solutions
- Wrap the method name in a list: `methods=["POST"]` (or a tuple/set).
- If the value comes from config as 'GET,POST', split it first: `methods=[m.strip() for m in value.split(',')]`.
- For class-based views, set `methods = ["GET", "POST"]` as a list on the class, not a string.
Example fix
# before
@app.route("/submit", methods="POST")
def submit():
...
# after
@app.route("/submit", methods=["POST"])
def submit():
... Defensive patterns
Strategy: type-guard
Validate before calling
methods = ['POST'] # a list/iterable of str, never a bare string assert not isinstance(methods, str) and all(isinstance(m, str) for m in methods)
Type guard
def is_valid_methods(methods):
return methods is None or (not isinstance(methods, str) and hasattr(methods, '__iter__') and all(isinstance(m, str) for m in methods)) Prevention
- Always pass methods as a list: methods=["POST"], never methods="POST"
- Uppercase strings only; avoid tuples of non-strings from config
- Wrap route registration helpers to normalize a single string into [string] if you accept both
When it happens
Trigger: `@app.route('/x', methods='POST')` or `app.add_url_rule('/x', view_func=f, methods='GET, POST')` — any place `methods` is passed as a bare string instead of a list/tuple/set of strings. Also triggers via a class-based view whose `methods` attribute was set to a string.
Common situations: Beginners writing `methods='POST'` by analogy with other string options; methods loaded from a config file or env var as a comma-separated string and passed through unsplit; typo dropping the brackets during a quick edit.
Related errors
- The setup method '{f_name}' can no longer be called on the a
- View function mapping is overwriting an existing endpoint fu
- 'endpoint' may not contain a dot '.' character.
- 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/50e2f4bc8a628739.json.
Report an issue: GitHub ↗.