{"record":{"id":"ecf8885406cf5a08","repo":"rubysherpas/paranoia","slug":"you-are-passing-an-instance-of-activerecord-base","errorCode":null,"errorMessage":"You are passing an instance of ActiveRecord::Base to `restore`. Please pass the id of the object by calling `.id`","messagePattern":"You are passing an instance of ActiveRecord::Base to `restore`\\. Please pass the id of the object by calling `\\.id`","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"lib/paranoia.rb","lineNumber":52,"sourceCode":"        return with_deleted.where.not(paranoia_column => paranoia_sentinel_value)\n      end\n      # if paranoia_sentinel_value is not null, then it is possible that\n      # some deleted rows will hold a null value in the paranoia column\n      # these will not match != sentinel value because \"NULL != value\" is\n      # NULL under the sql standard\n      # Scoping with the table_name is mandatory to avoid ambiguous errors when joining tables.\n      scoped_quoted_paranoia_column = \"#{connection.quote_table_name(self.table_name)}.#{connection.quote_column_name(paranoia_column)}\"\n      with_deleted.where(\"#{scoped_quoted_paranoia_column} IS NULL OR #{scoped_quoted_paranoia_column} != ?\", paranoia_sentinel_value)\n    end\n    alias_method :deleted, :only_deleted\n\n    # If you want to restore a record\n    def restore(id_or_ids, opts = {})\n      ids = Array(id_or_ids).flatten\n      any_object_instead_of_id = ids.any? { |id| ActiveRecord::Base === id }\n      if any_object_instead_of_id\n        ids.map! { |id| ActiveRecord::Base === id ? id.id : id }\n        ActiveSupport::Deprecation.warn(\"You are passing an instance of ActiveRecord::Base to `restore`. \" \\\n                                        \"Please pass the id of the object by calling `.id`\")\n      end\n      ids.map { |id| only_deleted.find(id).restore!(opts) }\n    end\n\n    def paranoia_destroy_attributes\n      {\n        paranoia_column => current_time_from_proper_timezone\n      }.merge(timestamp_attributes_with_current_time)\n    end\n\n    def timestamp_attributes_with_current_time\n      timestamp_attributes_for_update_in_model.each_with_object({}) { |attr,hash| hash[attr] = current_time_from_proper_timezone }\n    end\n  end\n\n  def paranoia_destroy\n    with_transaction_returning_status do","sourceCodeStart":34,"sourceCodeEnd":70,"githubUrl":"https://github.com/rubysherpas/paranoia/blob/a950fe498191ba01fc45d716e7157d1903ef30b7/lib/paranoia.rb#L34-L70","documentation":"The class method Model.restore(id_or_ids, opts) is an id-based API: it flattens the argument and runs only_deleted.find(id).restore!(opts) for each element. Since paranoia 2.2 it still accepts ActiveRecord::Base instances by coercing them via id.id, but emits an ActiveSupport::Deprecation warning asking you to pass .id, because a future major release removes the coercion. In suites configured to raise on deprecations (config.active_support.deprecation = :raise) the warning becomes a hard test failure.","triggerScenarios":"Calling Model.restore(record_instance) or Model.restore([instance_a, instance_b]) — any element of id_or_ids for which ActiveRecord::Base === id is true enters the warning branch at lib/paranoia.rb:52. Typical cases: restoring a single held object (deleted_post), bulk restores passing an array or relation of instances, and legacy call sites written against paranoia < 2.2 where passing an instance was silent.","commonSituations":"Upgrading paranoia from 2.1 to 2.2+ makes old tests (e.g. test_restore_on_object_return_self, test_multiple_restore, test_restore_with_associations) that pass instances start warning; CI environments that turn deprecation warnings into errors fail those tests; admin/bulk-restore features pass ActiveRecord objects or relations instead of ids.","solutions":["Pass ids instead of instances: Model.restore(record.id) for one record, Model.restore(records.map(&:id)) for many.","If you already hold the record, prefer the instance method — record.restore (optionally record.restore(opts)) — which restores it and returns self with no warning.","For records fetched from only_deleted, chain instance restore: Model.only_deleted.find(id).restore instead of the class method with an object.","Sweep the codebase for .restore( call sites whose argument is an ActiveRecord object (grep after a paranoia upgrade) and normalize them through an id-coercion helper."],"exampleFix":"# before\nParanoia.restore(deleted_post)               # deprecation: pass the id by calling .id\nParanoia.restore([post_a, post_b])          # same warning for array elements\n\n# after\ndeleted_post.restore                        # single record you hold: instance method\nParanoia.restore([post_a, post_b].map(&:id))  # class method: ids only","handlingStrategy":"type-guard","validationCode":"ids = Array(arg).map { |v| v.is_a?(ActiveRecord::Base) ? v.id : v }\nModel.restore(ids)","typeGuard":"def to_restore_ids(arg)\n  Array(arg).flatten.map { |v| v.is_a?(ActiveRecord::Base) ? v.id : v }\nend\n\n# usage: Model.restore(to_restore_ids(param))","tryCatchPattern":null,"preventionTips":["Treat the class-level restore as an ids-only API; wrap it with a coercion helper that maps instances to ids.","When you already hold the object, call the instance method record.restore instead of Model.restore(record).","Run the test suite with deprecation warnings visible and fix them right after upgrading paranoia, before the 3.x removal of instance coercion.","Grep for .restore( sites receiving ActiveRecord objects or relations whenever paranoia is bumped."],"tags":["paranoia","deprecation","soft-delete","rails","activerecord","restore"],"backgroundTag":"deprecation-warning","analyzedSha":"a950fe498191ba01fc45d716e7157d1903ef30b7","analyzedAt":"2026-08-23T15:28:42.766Z","schemaVersion":2},"datasetVersion":"2026-08-23T16:17:53.355Z"}