pallets/flask · error · AssertionError

The setup method ' ' can no longer be called on the…

Error message

The setup method '{f_name}' can no longer be called on the blueprint '{self.name}'. It has already been registered at least once, any changes will not be applied consistently.
Make sure all imports, decorators, functions, etc. needed to set up the blueprint are done before registering it.

What it means

Like the app guard, a Blueprint sets _got_registered_once=True once it has been registered on any app. Blueprint setup methods (@setupmethod on the blueprint: route, add_url_rule, before_request, errorhandler, etc.) call _check_setup_finished and raise AssertionError if called after the first registration, because deferred functions are replayed only at registration time.

Solutions

  1. Define all routes/handlers on the blueprint BEFORE calling app.register_blueprint(bp).
  2. Use a fresh blueprint instance per registration if you must mutate it (factory function returning a new Blueprint).
  3. For shared extension behavior, register additional blueprints rather than mutating an already-registered one.

Example fix

// before
app.register_blueprint(bp)
@bp.route('/late')  # raises
def late(): ...

// after
@bp.route('/late')
def late(): ...
app.register_blueprint(bp)
Defensive patterns

Strategy: validation

Validate before calling

def assert_bp_settable(bp):
    if getattr(bp, '_got_registered_once', False):
        raise RuntimeError(f'blueprint {bp.name!r} already registered; configure before register_blueprint')

Type guard

def is_bp_setup_open(bp) -> bool:
    return not getattr(bp, '_got_registered_once', False)

Try / catch

try:
    bp.add_url_rule('/x', view_func=fn)
except AssertionError as e:
    if 'blueprint' in str(e) and 'no longer be called' in str(e):
        # use a fresh blueprint instance and retry
        raise

Prevention

When it happens

Trigger: Registering the blueprint with app.register_blueprint(bp) and then calling bp.route(...)/bp.add_url_rule(...)/bp.errorhandler(...)/bp.before_app_request(...) afterwards; defining routes in a module imported lazily after registration.

Common situations: Plugin packages that register a blueprint then extend it; circular imports causing blueprint mutation after registration; tests that reuse one blueprint instance across multiple test app registrations and try to add routes between.

Related errors


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

Appendix: source

Thrown at src/flask/sansio/blueprints.py:215

        if "." in name:
            raise ValueError("'name' may not contain a dot '.' character.")

        self.name = name
        self.url_prefix = url_prefix
        self.subdomain = subdomain
        self.deferred_functions: list[DeferredSetupFunction] = []

        if url_defaults is None:
            url_defaults = {}

        self.url_values_defaults = url_defaults
        self.cli_group = cli_group
        self._blueprints: list[tuple[Blueprint, dict[str, t.Any]]] = []

    def _check_setup_finished(self, f_name: str) -> None:
        if self._got_registered_once:
            raise AssertionError(
                f"The setup method '{f_name}' can no longer be called on the blueprint"
                f" '{self.name}'. It has already been registered at least once, any"
                " changes will not be applied consistently.\n"
                "Make sure all imports, decorators, functions, etc. needed to set up"
                " the blueprint are done before registering it."
            )

    @setupmethod
    def record(self, func: DeferredSetupFunction) -> None:
        """Registers a function that is called when the blueprint is
        registered on the application.  This function is called with the
        state as argument as returned by the :meth:`make_setup_state`
        method.
        """
        self.deferred_functions.append(func)

    @setupmethod
    def record_once(self, func: DeferredSetupFunction) -> None:

View on GitHub (pinned to 3596b1ab61)