sqlalchemy/sqlalchemy · error · NotImplementedError
params() is not supported for INSERT/UPDATE/DELETE statement
Error message
params() is not supported for INSERT/UPDATE/DELETE statements. To set the values for an INSERT or UPDATE statement, use stmt.values(**parameters).
What it means
The base DML class (`UpdateBase`) defines `params()` purely to raise `NotImplementedError`; it is overridden only by `ValuesBase` (INSERT/UPDATE) where it actually supplies SET/VALUES. Calling `.params(...)` on a `delete()`, or calling it on an INSERT/UPDATE before the `ValuesBase` machinery is engaged, hits this stub. SQLAlchemy deliberately does not let `params()` set row values for DML — values go through `.values()`.
Source
Thrown at lib/sqlalchemy/sql/dml.py:477
proxy_key,
fallback_label_name,
c,
repeated,
) in (self._generate_columns_plus_names(False))
if is_column_element(c)
]
columns._populate_separate_keys(prox)
def params(self, *arg: Any, **kw: Any) -> NoReturn:
"""Set the parameters for the statement.
This method raises ``NotImplementedError`` on the base class,
and is overridden by :class:`.ValuesBase` to provide the
SET/VALUES clause of UPDATE and INSERT.
"""
raise NotImplementedError(
"params() is not supported for INSERT/UPDATE/DELETE statements."
" To set the values for an INSERT or UPDATE statement, use"
" stmt.values(**parameters)."
)
@_generative
def with_dialect_options(self, **opt: Any) -> Self:
"""Add dialect options to this INSERT/UPDATE/DELETE object.
e.g.::
upd = table.update().dialect_options(mysql_limit=10)
.. versionadded:: 1.4 - this method supersedes the dialect options
associated with the constructor.
"""View on GitHub (pinned to d9b44cb731)
Solutions
- For INSERT/UPDATE, replace `.params(**params)` with `.values(**params)` (the error message itself recommends this).
- For DELETE, remove the `.params()` call entirely; bind values via `where(table.c.id == bindparam('id'))` plus execution-time parameters, or use `bindparam` literals.
- If you need runtime-bound literal values inside a DML statement, use `bindparam('name', value=...)` inside `.values()` and pass the actual values at `connection.execute(stmt, params)`.
- For a Core SELECT, keep using `.params()` — this error only fires for DML; verify the statement type with `type(stmt)`.
Example fix
# before stmt = users.update().where(users.c.id == 5).params(name="bob") # raises: params() is not supported for INSERT/UPDATE/DELETE # after stmt = users.update().where(users.c.id == 5).values(name="bob")
Defensive patterns
Strategy: validation
Validate before calling
from sqlalchemy.sql.dml import Insert, Update
def set_parameters(stmt, parameters):
"""Route to the correct method: .values() for DML, .params() for SELECT."""
if isinstance(stmt, (Insert, Update)):
return stmt.values(**parameters)
# DELETE has no VALUES; pass params for execution-time bind instead
return stmt.params(**parameters) if hasattr(stmt, "params") else stmt
# usage:
# stmt = users.update().where(users.c.id == 1)
# stmt = set_parameters(stmt, {"name": "wendy"}) # -> stmt.values(...) Prevention
- Never call .params() on Insert/Update/Delete objects; DML uses .values(**params).
- When writing helper functions that accept arbitrary statements, branch on isinstance(stmt, (Insert, Update, Delete)) before choosing .params() vs .values().
- Remember DELETE has no SET clause — supply bind values at connection.execute() time instead.
- Keep a single helper (as above) so the routing rule lives in one place.
When it happens
Trigger: Calling `stmt = table.delete().where(...)` then `stmt.params(x=1)`; calling `.params()` on a raw `insert()`/`update()` object that has not been built via `ValuesBase`; using the Core/legacy `.params(bind=value)` style (a `select()` idiom) on a DML statement instead of `.values(...)`.
Common situations: Developers porting `select().params(...)` patterns to INSERT/UPDATE; mixing the old `bindparam`/`params` query style with 2.0-style DML builders; passing a dict of column values via `.params()` because it 'looks like' parameter binding; ORM bulk operations code that assumed `params()` works uniformly.
Related errors
- Extension {type(self).__name__} cannot be applied to update
- Extension {type(self).__name__} cannot be applied to delete
- Extension {type(self).__name__} cannot be applied to insert
- update parameter dictionary must not be empty
- update parameter must be a non-empty dictionary or a ColumnC
AI-assisted analysis of sqlalchemy/sqlalchemy@d9b44cb731 (2026-08-01).
Data as JSON: /data/errors/8ca4fd3f95e516e0.json.
Report an issue: GitHub.