pallets/flask · error · AssertionError
View function mapping is overwriting an existing endpoint…
Error message
View function mapping is overwriting an existing endpoint function: {endpoint} What it means
Flask stores view functions in a dict keyed by endpoint name (app.view_functions). add_url_rule checks if an endpoint already maps to a DIFFERENT callable; if so it raises AssertionError to prevent a silent override. Registering the same function again (e.g. re-import) is allowed.
Solutions
- Give each conflicting view a distinct endpoint via endpoint='unique_name', or rename one of the functions.
- Register blueprints with unique names (name= or name_prefix=) so blueprint endpoints do not collide with app-level endpoints.
- Check for duplicate route registration caused by importing the same module twice; deduplicate the imports.
- Inspect app.view_functions keys at startup to find the duplicate before it raises.
Example fix
// before
@app.route('/a')
def index(): ...
@app.route('/b') # default endpoint 'index' collides
def index(): ...
// after
@app.route('/a')
def index(): ...
@app.route('/b', endpoint='index_b')
def index_b(): ... Defensive patterns
Strategy: validation
Validate before calling
def assert_endpoint_free(app, endpoint):
if endpoint in app.view_functions:
raise ValueError(f'endpoint {endpoint!r} already registered to {app.view_functions[endpoint]!r}')
Type guard
def endpoint_is_unique(app, endpoint: str) -> bool:
return endpoint not in app.view_functions
Try / catch
try:
app.add_url_rule('/x', endpoint='x', view_func=fn)
except AssertionError as e:
if 'overwriting an existing endpoint' in str(e):
# choose a different endpoint name and retry
raise Prevention
- Always pass explicit endpoint= for route factories or dynamic routes.
- Dump app.view_functions keys at startup to catch collisions early.
- Keep blueprint and app endpoint namespaces disjoint with unique prefixes.
When it happens
Trigger: Two different view functions resolving to the same endpoint (default endpoint = view_func.__name__); explicitly passing endpoint='x' to two distinct functions; a blueprint and an app route colliding on endpoint name; functools.partial or lambda views colliding on default name.
Common situations: Naming two views the same (e.g. two index functions in different modules), Flask's default endpoint-from-view-func colliding, blueprint registration without name_prefix producing colliding dotted endpoints, double-decorating the same function path.
Related errors
- Allowed methods must be a list of strings, for example…
- 'endpoint' may not contain a dot '.' character.
- The name ' ' is already registered for blueprint . Use…
- The setup method ' ' can no longer be called on the…
- The setup method ' ' can no longer be called on the…
AI-assisted analysis of pallets/flask@3596b1ab61 (2026-08-11).
Data as JSON: /api/errors/d7d0b9069440b499.
Report an issue: GitHub.
Appendix: source
Thrown at src/flask/sansio/app.py:657
provide_automatic_options = (
"OPTIONS" not in methods
and self.config["PROVIDE_AUTOMATIC_OPTIONS"]
)
if provide_automatic_options:
required_methods.add("OPTIONS")
# Add the required methods now.
methods |= required_methods
rule_obj = self.url_rule_class(rule, methods=methods, **options)
rule_obj.provide_automatic_options = provide_automatic_options # type: ignore[attr-defined]
self.url_map.add(rule_obj)
if view_func is not None:
old_func = self.view_functions.get(endpoint)
if old_func is not None and old_func != view_func:
raise AssertionError(
"View function mapping is overwriting an existing"
f" endpoint function: {endpoint}"
)
self.view_functions[endpoint] = view_func
@t.overload
def template_filter(self, name: T_template_filter) -> T_template_filter: ...
@t.overload
def template_filter(
self, name: str | None = None
) -> t.Callable[[T_template_filter], T_template_filter]: ...
@setupmethod
def template_filter(
self, name: T_template_filter | str | None = None
) -> T_template_filter | t.Callable[[T_template_filter], T_template_filter]:
"""Decorate a function to register it as a custom Jinja filter. The name
is optional. The decorator may be used without parentheses.
View on GitHub (pinned to 3596b1ab61)