{"record":{"id":"08ad7d36cea98528","repo":"sqlalchemy/alembic","slug":"list-expected","errorCode":null,"errorMessage":"List expected","messagePattern":"List expected","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"alembic/ddl/impl.py","lineNumber":484,"sourceCode":"        self._exec(schema.SetTableComment(table))\n\n    def drop_table_comment(self, table: Table) -> None:\n        self._exec(schema.DropTableComment(table))\n\n    def create_column_comment(self, column: Column[Any]) -> None:\n        self._exec(schema.SetColumnComment(column))\n\n    def drop_index(self, index: Index, **kw: Any) -> None:\n        self._exec(schema.DropIndex(index, **kw))\n\n    def bulk_insert(\n        self,\n        table: TableClause | Table,\n        rows: list[dict],\n        multiinsert: bool = True,\n    ) -> None:\n        if not isinstance(rows, list):\n            raise TypeError(\"List expected\")\n        elif rows and not isinstance(rows[0], dict):\n            raise TypeError(\"List of dictionaries expected\")\n        if self.as_sql:\n            for row in rows:\n                self._exec(\n                    table.insert()\n                    .inline()\n                    .values(\n                        **{\n                            k: (\n                                sqla_compat._literal_bindparam(\n                                    k, v, type_=table.c[k].type\n                                )\n                                if not isinstance(\n                                    v, sqla_compat._literal_bindparam\n                                )\n                                else v\n                            )","sourceCodeStart":466,"sourceCodeEnd":502,"githubUrl":"https://github.com/sqlalchemy/alembic/blob/5551b5d35f985c99cb8f1af2b3c526b050e4c059/alembic/ddl/impl.py#L466-L502","documentation":"DefaultImpl.bulk_insert raises TypeError when the rows argument is not a Python list. bulk_insert expects a list of row dictionaries so it can build an INSERT (or multi-row INSERT); passing a single dict, tuple, or other iterable is rejected.","triggerScenarios":"Calling op.bulk_insert(table, {'col': 1}) (a single dict instead of a list); passing a generator, tuple, or ORM object instead of a list of dicts.","commonSituations":"Authors forgetting to wrap a single row in a list; passing a SQLAlchemy Result or an ORM model instance; code migrated from a function that yielded rows.","solutions":["Wrap a single row in a list: op.bulk_insert(table, [{'col': 1}]).","Convert any iterable to a list first: op.bulk_insert(table, list(rows)).","Ensure each element is a dict mapping column names to values."],"exampleFix":"# before\nop.bulk_insert(accounts_table, {'name': 'acct', 'active': True})\n\n# after\nop.bulk_insert(accounts_table, [{'name': 'acct', 'active': True}])","handlingStrategy":"type-guard","validationCode":"def safe_bulk_insert(op, table, rows):\n    if not isinstance(rows, list):\n        raise TypeError('rows must be a list of dicts')\n    op.bulk_insert(table, rows)","typeGuard":"def is_list_of_rows(value) -> bool:\n    return isinstance(value, list)","tryCatchPattern":null,"preventionTips":["Always pass a list of dicts to op.bulk_insert.","Wrap single rows: [row].","Add a type assertion in shared migration helpers."],"tags":["bulk-insert","ddl","type-error","data-migration"],"backgroundTag":null,"analyzedSha":"5551b5d35f985c99cb8f1af2b3c526b050e4c059","analyzedAt":"2026-08-11T01:38:46.612Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}