{"id":"82bd8acab32bea29","repo":"sqlalchemy/alembic","slug":"can-t-send-params-and-multiparams-at-the-same-time","errorCode":null,"errorMessage":"Can't send params and multiparams at the same time","messagePattern":"Can't send params and multiparams at the same time","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"alembic/ddl/impl.py","lineNumber":245,"sourceCode":"            else:\n                compile_kw = {}\n\n            if TYPE_CHECKING:\n                assert isinstance(construct, ClauseElement)\n            compiled = construct.compile(dialect=self.dialect, **compile_kw)\n            self.static_output(\n                str(compiled).replace(\"\\t\", \"    \").strip()\n                + self.command_terminator\n            )\n            return None\n        else:\n            conn = self.connection\n            assert conn is not None\n            if execution_options:\n                conn = conn.execution_options(**execution_options)\n\n            if params and multiparams is not None:\n                raise TypeError(\n                    \"Can't send params and multiparams at the same time\"\n                )\n\n            if multiparams:\n                return conn.execute(construct, multiparams)\n            else:\n                return conn.execute(construct, params)\n\n    def execute(\n        self,\n        sql: Executable | str,\n        execution_options: dict[str, Any] | None = None,\n    ) -> None:\n        self._exec(sql, execution_options)\n\n    def alter_column(\n        self,\n        table_name: str,","sourceCodeStart":227,"sourceCodeEnd":263,"githubUrl":"https://github.com/sqlalchemy/alembic/blob/44fb3450330204b222ff05135e1fbbbdb28c44db/alembic/ddl/impl.py#L227-L263","documentation":"The internal _exec passes either params (a single dict for one row) or multiparams (a list of dicts for executemany). SQLAlchemy's connection.execute rejects passing both at once, so Alembic pre-empts that with a clear error. This indicates a caller that built both arguments simultaneously.","triggerScenarios":"A caller of impl._exec (internal API) or a custom Op that forwards both params=dict(...) and multiparams=[...] simultaneously; op.bulk_insert routes correctly into multiparams, so this surfaces via direct _exec use or custom operations.","commonSituations":"A custom op or migration that builds parameters dynamically and accidentally assigns both params and multiparams; mixing a single-row default with a multi-row list; calling impl._exec directly with both kwargs.","solutions":["Pass exactly one of params= or multiparams=. For multiple rows use multiparams=[{...},{...}].","For a single row use params={'k': v} (or omit and pass the dict as the sole argument).","If building dynamically, branch: if isinstance(rows, list) and rows and isinstance(rows[0], dict): use multiparams=rows else params=rows."],"exampleFix":"// before\nimpl._exec(stmt, params={'a': 1}, multiparams=[{'a': 1}, {'a': 2}])\n// after\nimpl._exec(stmt, multiparams=[{'a': 1}, {'a': 2}])","handlingStrategy":"validation","validationCode":"def safe_exec(impl, construct, params=None, multiparams=None):\n    assert not (params and multiparams is not None), \"Pass params OR multiparams, not both\"\n    return impl._exec(construct, params=params or util.immutabledict(), multiparams=multiparams)","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never pass both params= and multiparams= to _exec/execute.","For multi-row inserts use multiparams=[dict, ...] only.","Wrap lower-level _exec calls in a helper that enforces mutual exclusion."],"tags":["execution","parameters","internal"],"analyzedSha":"44fb3450330204b222ff05135e1fbbbdb28c44db","analyzedAt":"2026-08-04T19:57:10.248Z","schemaVersion":2}