sqlalchemy/alembic · error · TypeError

SQL parameters not allowed with as_sql

Error message

SQL parameters not allowed with as_sql

What it means

When the migration context is in as_sql (offline) mode, the impl writes SQL text to a file/stream rather than executing against a database. Bind parameters cannot be rendered to a script because there is no DB to substitute them; only literal SQL (optionally with literal_binds) is emitted. Passing params/multiparams in this mode is therefore rejected.

Source

Thrown at alembic/ddl/impl.py:220

        """

    @property
    def bind(self) -> Connection | None:
        return self.connection

    def _exec(
        self,
        construct: Executable | str,
        execution_options: Mapping[str, Any] | None = None,
        multiparams: Sequence[Mapping[str, Any]] | None = None,
        params: Mapping[str, Any] = util.immutabledict(),
    ) -> CursorResult | None:
        if isinstance(construct, str):
            construct = text(construct)
        if self.as_sql:
            if multiparams is not None or params:
                raise TypeError("SQL parameters not allowed with as_sql")

            compile_kw: dict[str, Any]
            if self.literal_binds and not isinstance(
                construct, schema.DDLElement
            ):
                compile_kw = dict(compile_kwargs={"literal_binds": True})
            else:
                compile_kw = {}

            if TYPE_CHECKING:
                assert isinstance(construct, ClauseElement)
            compiled = construct.compile(dialect=self.dialect, **compile_kw)
            self.static_output(
                str(compiled).replace("\t", "    ").strip()
                + self.command_terminator
            )
            return None
        else:

View on GitHub (pinned to 44fb345033)

Solutions

  1. Run the migration online (remove --sql / as_sql) so bind parameters can be executed.
  2. For offline SQL, inline the values: op.execute("UPDATE t SET x = 1").
  3. Set literal_binds=True in the migration context if you need parameterized SQL rendered with literal values.

Example fix

// before
op.execute(sa.text('UPDATE users SET active = :v'), params={'v': True})
# run as: alembic upgrade head --sql  -> TypeError
// after
op.execute('UPDATE users SET active = true')
Defensive patterns

Strategy: validation

Validate before calling

if context.is_offline_mode():
    assert not params and multiparams is None, "Bind params not allowed in as_sql mode; inline values instead."

Try / catch

try:
    op.execute(text('...'), params=p)
except TypeError as e:
    if 'as_sql' in str(e):
        op.execute(render_literal_sql(stmt, p))
    else: raise

Prevention

When it happens

Trigger: Calling op.execute(text('...'), params=...) or op.bulk_insert(...) inside an offline (as_sql=True) migration where the operation carries bind parameters; invoking 'alembic upgrade head --sql' over a migration that uses parameterized statements.

Common situations: A migration written for online use that calls op.execute(sa.text('UPDATE t SET x=:x'), params={'x': 1}); running it in offline SQL dump mode; using bulk_insert in SQL generation mode with non-literal values without literal_binds.

Related errors


AI-assisted analysis of sqlalchemy/alembic@44fb345033 (2026-08-04). Data as JSON: /data/errors/0bc6e00142b7ba20.json. Report an issue: GitHub.