{"id":"b6643d8f53aa7bec","repo":"sqlalchemy/alembic","slug":"operation-is-not-reversible-original-column-is-no","errorCode":null,"errorMessage":"operation is not reversible; original column is not present","messagePattern":"operation is not reversible; original column is not present","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"alembic/operations/ops.py","lineNumber":2310,"sourceCode":"        super().__init__(table_name, schema=schema)\n        self.column_name = column_name\n        self.kw = kw\n        self.if_exists = if_exists\n        self._reverse = _reverse\n\n    def to_diff_tuple(\n        self,\n    ) -> tuple[str, str | None, str, Column[Any]]:\n        return (\n            \"remove_column\",\n            self.schema,\n            self.table_name,\n            self.to_column(),\n        )\n\n    def reverse(self) -> AddColumnOp:\n        if self._reverse is None:\n            raise ValueError(\n                \"operation is not reversible; \"\n                \"original column is not present\"\n            )\n\n        op = AddColumnOp.from_column_and_tablename(\n            self.schema, self.table_name, self._reverse.column\n        )\n        op.if_not_exists = self.if_exists\n        return op\n\n    @classmethod\n    def from_column_and_tablename(\n        cls,\n        schema: str | None,\n        tname: str,\n        col: Column[Any],\n    ) -> DropColumnOp:\n        return cls(","sourceCodeStart":2292,"sourceCodeEnd":2328,"githubUrl":"https://github.com/sqlalchemy/alembic/blob/44fb3450330204b222ff05135e1fbbbdb28c44db/alembic/operations/ops.py#L2292-L2328","documentation":"Raised by DropColumnOp.reverse() when self._reverse is None. reverse() needs the original AddColumnOp (captured by from_column_and_tablename) to rebuild an add-column operation for downgrade. A DropColumnOp built directly from just (table, column_name) carries no column definition and cannot be inverted, so reversal is refused.","triggerScenarios":"Constructing DropColumnOp('t', 'c') directly and calling .reverse() on it; the autogenerate reverser attempting to invert a manually-created drop-column op; a process_revision_directives hook building drop ops without column metadata.","commonSituations":"Custom migration tooling that creates DropColumnOp from a name string rather than from a Column object; downgrade generation hitting an op that lacks reverse info.","solutions":["Build the op via DropColumnOp.from_column_and_tablename(schema, table, column) so _reverse is attached.","Use op.drop_column('t','c') for forward execution (reverse() is only needed when generating downgrades), and provide the column metadata when reversibility is required.","In custom rewriter hooks, attach the AddColumnOp reverse before relying on .reverse()."],"exampleFix":"// before\nop_obj = DropColumnOp('user', 'email')\nop_obj.reverse()  # raises ValueError\n\n// after\nfrom sqlalchemy import Column, String\nop_obj = DropColumnOp.from_column_and_tablename(\n    None, 'user', Column('email', String(50))\n)\nop_obj.reverse()  # ok, produces AddColumnOp","handlingStrategy":"validation","validationCode":"from alembic.operations.ops import DropColumnOp\nfrom sqlalchemy import Column\n\ndef build_reversible_drop_col(schema, table, col: Column):\n    return DropColumnOp.from_column_and_tablename(schema, table, col)","typeGuard":"from alembic.operations.ops import DropColumnOp\ndef drop_col_is_reversible(op_obj: DropColumnOp) -> bool:\n    return getattr(op_obj, '_reverse', None) is not None","tryCatchPattern":"try:\n    op_obj.reverse()\nexcept ValueError as e:\n    if 'operation is not reversible' in str(e):\n        op_obj = DropColumnOp.from_column_and_tablename(schema, table, column)\n    else:\n        raise","preventionTips":["Use from_column_and_tablename() so drop-column ops carry reverse info.","Provide full Column metadata when downgrade generation is required.","Don't construct DropColumnOp from a bare name in reversible contexts."],"tags":["alembic","operations","columns","migrations","internals"],"analyzedSha":"44fb3450330204b222ff05135e1fbbbdb28c44db","analyzedAt":"2026-08-04T19:57:10.248Z","schemaVersion":2}