sqlalchemy/alembic · error · NameError

Can't invoke function

Error message

Can't invoke function '%s', as the proxy object has not yet been established for the Alembic '%s' class.  Try placing this code inside a callable.

What it means

Raised as NameError by the module-level proxy mechanism in ModuleClsMeta when a proxied function (e.g. op.batch_alter_table, commands.upgrade) is invoked before _install_proxy() has been called. Alembic uses lazy proxies so that 'from alembic import op' works at import time — the proxy defers to a real object that is installed at runtime when a migration context is active. Calling these at import time or outside a migration context triggers this.

Solutions

  1. Move all op.X() calls inside the upgrade() or downgrade() functions, not at module level.
  2. If testing migrations, use alembic.testing or set up an actual MigrationContext before invoking migration code.
  3. Ensure 'from alembic import op' is only used inside migration scripts that are run by the alembic runner.
  4. Check that the env.py file properly calls context.configure() and context.run_migrations() which install the proxy.

Example fix

# before
from alembic import op
import sqlalchemy as sa

op.add_column('users', sa.Column('email', sa.String()))  # module-level!

def upgrade():
    pass

# after
from alembic import op
import sqlalchemy as sa

def upgrade():
    op.add_column('users', sa.Column('email', sa.String()))
Defensive patterns

Strategy: validation

Validate before calling

import inspect

def validate_op_calls_in_functions(module_source):
    """Check that op.X() calls are inside function definitions, not at module level."""
    import ast
    tree = ast.parse(module_source)
    for node in ast.iter_child_nodes(tree):
        if isinstance(node, ast.Expr):
            if isinstance(node.value, ast.Call):
                func = node.value.func
                if isinstance(func, ast.Attribute) and isinstance(func.value, ast.Name) and func.value.id == 'op':
                    return False  # module-level op call found
    return True

Prevention

When it happens

Trigger: Calling op.X() or commands.X() at Python module top level / import time rather than inside a migration function (upgrade/downgrade). Also triggered by running migration code outside the alembic runner context (e.g. importing a migration module directly and calling upgrade()). The proxy function at langhelpers.py:197-201 tries to read _proxy and catches NameError, calling _name_error.

Common situations: Writing migration helper logic at module scope instead of inside def upgrade(). Importing alembic.op in a utility module and calling it from outside a migration. Testing migration code by importing it directly without setting up a MigrationContext. Refactoring that accidentally moves op calls outside function scope.

Related errors


AI-assisted analysis of sqlalchemy/alembic@5551b5d35f (2026-08-11). Data as JSON: /api/errors/037b52f27caec2c2. Report an issue: GitHub.

Appendix: source

Thrown at alembic/util/langhelpers.py:130

            meth = getattr(cls, methname)
            if callable(meth):
                locals_[methname] = cls._create_method_proxy(
                    methname, globals_, locals_
                )
            else:
                attr_names.add(methname)

    @classmethod
    def _create_method_proxy(
        cls,
        name: str,
        globals_: MutableMapping[str, Any],
        locals_: MutableMapping[str, Any],
    ) -> Callable[..., Any]:
        fn = getattr(cls, name)

        def _name_error(name: str, from_: Exception) -> NoReturn:
            raise NameError(
                "Can't invoke function '%s', as the proxy object has "
                "not yet been "
                "established for the Alembic '%s' class.  "
                "Try placing this code inside a callable."
                % (name, cls.__name__)
            ) from from_

        globals_["_name_error"] = _name_error

        translations = getattr(fn, "_legacy_translations", [])
        if translations:
            spec = inspect_getfullargspec(fn)
            if spec[0] and spec[0][0] == "self":
                spec[0].pop(0)

            outer_args = inner_args = "*args, **kw"
            translate_str = "args, kw = _translate(%r, %r, %r, args, kw)" % (
                fn.__name__,

View on GitHub (pinned to 5551b5d35f)