{"record":{"id":"b7b2c6fbc7fcb673","repo":"norman/friendly_id","slug":"is-reserved","errorCode":null,"errorMessage":"is reserved","messagePattern":"is reserved","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"lib/friendly_id/reserved.rb","lineNumber":37,"sourceCode":"  #     class Person < ActiveRecord::Base\n  #       extend FriendlyId\n  #       friendly_id :name, use: :slugged\n  #\n  #       after_validation :move_friendly_id_error_to_name\n  #\n  #       def move_friendly_id_error_to_name\n  #         errors.add :name, *errors.delete(:friendly_id) if errors[:friendly_id].present?\n  #       end\n  #     end\n  #\n  # @guide end\n  module Reserved\n    # When included, this module adds configuration options to the model class's\n    # friendly_id_config.\n    def self.included(model_class)\n      model_class.class_eval do\n        friendly_id_config.class.send :include, Reserved::Configuration\n        validates_exclusion_of :friendly_id, in: ->(_) {\n          friendly_id_config.reserved_words || []\n        }\n      end\n    end\n\n    # This module adds the `:reserved_words` configuration option to\n    # {FriendlyId::Configuration FriendlyId::Configuration}.\n    module Configuration\n      attr_accessor :reserved_words\n      attr_accessor :treat_reserved_as_conflict\n    end\n  end\nend\n","sourceCodeStart":19,"sourceCodeEnd":51,"githubUrl":"https://github.com/norman/friendly_id/blob/4699c9791db955dfa9929af0f38a4059af550026/lib/friendly_id/reserved.rb#L19-L51","documentation":"FriendlyId::Reserved (lib/friendly_id/reserved.rb:37) adds `validates_exclusion_of :friendly_id` against `friendly_id_config.reserved_words`, so a save fails with \"is reserved\" on the :friendly_id attribute whenever the resolved slug equals a reserved word. The Reserved module is active by default (`FriendlyId.defaults` does `config.use :reserved`), and the Rails generator's initializer reserves route-colliding words such as \"new\", \"edit\", \"index\", \"admin\", \"login\" (lib/friendly_id/initializer.rb:19). It is a validation failure, not an exception: `record.save` returns false and `record.valid?` is false.","triggerScenarios":"Creating or updating a record whose slug resolves to a reserved word — e.g. with the generated initializer `Post.create(title: \"New\")` produces slug \"new\", which is in the reserved list, so `save`/`valid?` adds \"is reserved\" to errors[:friendly_id]. Also triggered by assigning `friendly_id` directly to a reserved value, and typically happens when `treat_reserved_as_conflict` is not enabled, because the slugged generator then proposes the reserved slug instead of sequencing past it. Any model using FriendlyId inherits the Reserved validation via the default defaults block even if the author never asked for it.","commonSituations":"Scaffolded CRUD forms show nothing when this fires, because the error sits on :friendly_id which has no form field (the guide in reserved.rb shows moving it to :name with an after_validation hook); single-word titles like \"admin\" or \"new\"; teams editing the initializer's reserved list and forgetting dependent models; seed files and test fixtures that hardcode reserved slugs; keeping the default reserved list even though those words don't collide with the app's actual routes.","solutions":["Set `config.treat_reserved_as_conflict = true` in the FriendlyId initializer so the slug generator treats reserved words as taken and appends a sequence (\"new-2\") instead of failing validation.","Provide fallback candidates so validation can step past the reserved word: `friendly_id :name, use: [:slugged, :cached_candidates], candidates: [:name, [:name, :id]]`.","Trim `reserved_words` to words that actually collide with your routes — e.g. drop \"admin\" if it is not a top-level slug in your app.","When assigning friendly_id manually, check the value against `Model.friendly_id_config.reserved_words` (or suffix it) before assignment.","Surface the error to users: add an `after_validation :move_friendly_id_error_to_name` callback that re-adds errors[:friendly_id] onto the name attribute, as shown in reserved.rb's guide."],"exampleFix":"# before (config/initializers/friendly_id.rb)\nFriendlyId.defaults do |config|\n  config.reserved_words = %w[new edit index session login logout users admin]\nend\n# Post.create!(title: \"New\")\n# => ActiveRecord::RecordInvalid: validation failed: Friendly ID is reserved\n\n# after\nFriendlyId.defaults do |config|\n  config.reserved_words = %w[new edit index]\n  config.treat_reserved_as_conflict = true\nend\n# Post.create!(title: \"New\").slug # => \"new-2\"","handlingStrategy":"validation","validationCode":"slug = record.title.parameterize\nreserved = Post.friendly_id_config.reserved_words || []\nslug = \"#{slug}-2\" if reserved.include?(slug) # or pick another candidate\n# or, after the fact:\n# record.valid? && record.errors[:friendly_id].include?(\"is reserved\")","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Set `treat_reserved_as_conflict = true` so the generator sequences past reserved words instead of failing validation.","Keep the reserved_words list minimal and reviewed — only words that collide with real routes.","Cover reserved-word titles with model specs so the failure is caught in CI, not production.","Move the :friendly_id error onto a real attribute for form display via after_validation.","Never hardcode reserved slugs in seeds or fixtures."],"tags":["ruby","rails","friendly-id","validation","slug","reserved-words"],"backgroundTag":"model-validation-failed","analyzedSha":"4699c9791db955dfa9929af0f38a4059af550026","analyzedAt":"2026-08-21T19:17:35.279Z","schemaVersion":2},"datasetVersion":"2026-08-22T04:17:13.399Z"}