{"record":{"id":"0a8d9114bae9ed3c","repo":"scenic-views/scenic","slug":"method-is-reversible-only-if-given-a-revert-to","errorCode":null,"errorMessage":"#{method} is reversible only if given a revert_to_version","messagePattern":"#(.+?) is reversible only if given a revert_to_version","errorType":"exception","errorClass":"ActiveRecord::IrreversibleMigration","httpStatus":null,"severity":"error","filePath":"lib/scenic/command_recorder.rb","lineNumber":50,"sourceCode":"      perform_scenic_inversion(:create_view, args)\n    end\n\n    def invert_update_view(args)\n      perform_scenic_inversion(:update_view, args)\n    end\n\n    def invert_replace_view(args)\n      perform_scenic_inversion(:replace_view, args)\n    end\n\n    private\n\n    def perform_scenic_inversion(method, args)\n      scenic_args = StatementArguments.new(args)\n\n      if scenic_args.revert_to_version.nil?\n        message = \"#{method} is reversible only if given a revert_to_version\"\n        raise ActiveRecord::IrreversibleMigration, message\n      end\n\n      [method, scenic_args.invert_version.to_a]\n    end\n  end\nend\n","sourceCodeStart":32,"sourceCodeEnd":57,"githubUrl":"https://github.com/scenic-views/scenic/blob/f2162dbddb0fb0eb5d4b04d640dcec5303a387e8/lib/scenic/command_recorder.rb#L32-L57","documentation":"When rolling back, ActiveRecord's command recorder replays recorded statements in reverse, and Scenic's invert_drop_view, invert_update_view, and invert_replace_view all funnel into perform_scenic_inversion. That method needs the revert_to_version keyword to know which db/views definition to restore; without it there is no way to reconstruct the prior view, so it raises ActiveRecord::IrreversibleMigration ('<method> is reversible only if given a revert_to_version') instead of guessing.","triggerScenarios":"Running rails db:rollback, db:migrate:down VERSION=..., db:migrate:redo, or a revert block after a migration used drop_view :searches, update_view :searches, version: 2, or replace_view :searches, version: 2 without a revert_to_version keyword.","commonSituations":"Hand-written migrations that encode only the forward path; developers who pair revert_to_version with update_view but forget it on drop_view; CI pipelines that smoke-test with db:migrate:redo and fail on the rollback leg.","solutions":["Edit the migration to add revert_to_version naming the version that existed before the change (drop_view :searches, revert_to_version: 3; update_view :searches, version: 2, revert_to_version: 1), then re-run db:rollback - the recorder reads the migration file at rollback time, so editing after the up-run is safe","If the rollback already aborted, fix the file and rerun the same db:rollback or db:migrate:down command; nothing ran, so no schema repair is needed","If the change is intentionally one-way, stop rolling back past it: bring the schema forward again with db:migrate instead"],"exampleFix":"# before\ndef change\n  drop_view :searches\nend\n# db:rollback raises IrreversibleMigration\n\n# after\ndef change\n  drop_view :searches, revert_to_version: 3\nend","handlingStrategy":"validation","validationCode":"Dir[Rails.root.join('db/migrate/*.rb')].each do |file|\n  src = File.read(file)\n  next unless src =~ /(drop|update|replace)_view/\n  abort file + ': scenic view change lacks revert_to_version (irreversible)' unless src.include?('revert_to_version:')\nend","typeGuard":null,"tryCatchPattern":"begin\n  Rake::Task['db:rollback'].invoke\nrescue ActiveRecord::IrreversibleMigration => e\n  puts 'rollback aborted: ' + e.message\nend","preventionTips":["Treat revert_to_version as mandatory whenever writing drop_view, update_view, or replace_view","Add a CI check that fails migrations containing scenic view statements without revert_to_version","Run db:migrate:redo locally before pushing so the rollback leg is exercised"],"tags":["rails","migration","rollback","scenic","active-record"],"backgroundTag":"irreversible-migration","analyzedSha":"f2162dbddb0fb0eb5d4b04d640dcec5303a387e8","analyzedAt":"2026-08-23T09:20:35.041Z","schemaVersion":2},"datasetVersion":"2026-08-23T13:39:53.451Z"}