{"record":{"id":"bb84bbfe710253b1","repo":"heartcombo/devise","slug":"mapping-omniauth-callbacks-on-a-resource-that-is-n","errorCode":null,"errorMessage":"Mapping omniauth_callbacks on a resource that is not omniauthable\nPlease add `devise :omniauthable` to the `#{mapping.class_name}` model","messagePattern":"Mapping omniauth_callbacks on a resource that is not omniauthable\nPlease add `devise :omniauthable` to the `#(.+?)` model","errorType":"exception","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"lib/devise/rails/routes.rb","lineNumber":258,"sourceCode":"\n      resources.each do |resource|\n        mapping = Devise.add_mapping(resource, options)\n\n        begin\n          raise_no_devise_method_error!(mapping.class_name) unless mapping.to.respond_to?(:devise)\n        rescue NameError => e\n          raise unless mapping.class_name == resource.to_s.classify\n          warn \"[WARNING] You provided devise_for #{resource.inspect} but there is \" \\\n            \"no model #{mapping.class_name} defined in your application\"\n          next\n        rescue NoMethodError => e\n          raise unless e.message.include?(\"undefined method `devise'\")\n          raise_no_devise_method_error!(mapping.class_name)\n        end\n\n        if options[:controllers] && options[:controllers][:omniauth_callbacks]\n          unless mapping.omniauthable?\n            raise ArgumentError, \"Mapping omniauth_callbacks on a resource that is not omniauthable\\n\" \\\n              \"Please add `devise :omniauthable` to the `#{mapping.class_name}` model\"\n          end\n        end\n\n        routes = mapping.used_routes\n\n        devise_scope mapping.name do\n          with_devise_exclusive_scope mapping.fullpath, mapping.name, options do\n            routes.each { |mod| send(\"devise_#{mod}\", mapping, mapping.controllers) }\n          end\n        end\n      end\n    end\n\n    # Allow you to add authentication request from the router.\n    # Takes an optional scope and block to provide constraints\n    # on the model instance itself.\n    #","sourceCodeStart":240,"sourceCodeEnd":276,"githubUrl":"https://github.com/heartcombo/devise/blob/372b295fe6f63b4af3269f5dcd51a18c0bc2016c/lib/devise/rails/routes.rb#L240-L276","documentation":"When devise_for is given controllers: { omniauth_callbacks: \"...\" }, Devise validates at route-draw time that the mapped model actually includes the :omniauthable module (mapping.omniauthable?). The omniauth_callbacks controller only makes sense for an omniauthable resource — its routes (/users/auth/:provider, callback routes) are generated from the model's declared omniauth_providers. If the model lacks devise :omniauthable, route drawing raises ArgumentError immediately at boot with the model class name in the message.","triggerScenarios":"config/routes.rb contains devise_for :users, controllers: { omniauth_callbacks: \"users/omniauth_callbacks\" } while app/models/user.rb only has devise :database_authenticatable, :registerable, :recoverable (no :omniauthable); the :omniauthable module was removed during a refactor but the controllers hash in routes.rb was left in place; routes were copied from an OAuth-enabled app into one whose model was never updated.","commonSituations":"Incrementally enabling OAuth (developer wires routes/controller first, model second); cleaning up unused Devise modules from a model without checking routes.rb; copying devise_for lines between apps or engines; merging branch A's routes with branch B's model.","solutions":["Add the module to the model together with its providers: devise :omniauthable, omniauth_providers: [:google_oauth2] in app/models/user.rb (plus the matching config.omniauth entries and omniauth-* gems), then restart.","If OAuth is not actually used, remove the omniauth_callbacks entry (or the whole controllers: hash if it is the only override) from devise_for in config/routes.rb.","If you are mid-migration, comment out the controllers override until the model module and provider gems are in place, then restore it in the same commit.","Verify after boot with rails routes | grep omniauth_callbacks that the provider routes now exist."],"exampleFix":"# before\n# config/routes.rb\ndevise_for :users, controllers: { omniauth_callbacks: \"users/omniauth_callbacks\" }\n\n# app/models/user.rb\nclass User < ApplicationRecord\n  devise :database_authenticatable, :registerable, :recoverable\n  # -> ArgumentError: resource is not omniauthable\nend\n\n# after\n# app/models/user.rb\nclass User < ApplicationRecord\n  devise :database_authenticatable, :registerable, :recoverable,\n         :omniauthable, omniauth_providers: [:google_oauth2]\nend\n\n# Gemfile (provider gem must also be present)\ngem \"omniauth-google-oauth2\"","handlingStrategy":"validation","validationCode":"# Guard the route declaration against the model's actual modules (routes.rb):\ndevise_for :users,\n  controllers: (User.devise_modules.include?(:omniauthable) ?\n    { omniauth_callbacks: \"users/omniauth_callbacks\" } : {})\n\n# Or in a boot check after routes load:\nRails.application.config.after_initialize do\n  Devise.mappings.each_value do |m|\n    next unless m.controllers.key?(:omniauth_callbacks)\n    raise \"#{m.class_name} needs devise :omniauthable\" unless m.omniauthable?\n  end\nend","typeGuard":"def omniauthable_resource?(klass)\n  klass.respond_to?(:devise_modules) && klass.devise_modules.include?(:omniauthable)\nend","tryCatchPattern":null,"preventionTips":["Change the model (add devise :omniauthable, omniauth_providers: [...]) and the devise_for controllers override in the same commit.","When removing Devise modules from a model, grep routes.rb for the matching controllers: entries in the same change.","Run rails routes after any OAuth-related change — omniauth_callback routes appearing confirms model and routes agree.","Keep OAuth setup scripted (one PR/commit: Gemfile + initializer + model + routes) so the pieces never drift."],"tags":["devise","rails","routing","omniauth","configuration-mismatch","boot-error"],"backgroundTag":"devise-omniauthable-not-enabled","analyzedSha":"372b295fe6f63b4af3269f5dcd51a18c0bc2016c","analyzedAt":"2026-08-21T11:55:31.116Z","schemaVersion":2},"datasetVersion":"2026-08-21T13:17:26.733Z"}