{"record":{"id":"dd47f7eca1716f99","repo":"lynndylanhurley/devise_token_auth","slug":"not-found","errorCode":null,"errorMessage":"Not Found","messagePattern":"Not Found","errorType":"http","errorClass":"ActionController::RoutingError","httpStatus":404,"severity":"error","filePath":"app/controllers/devise_token_auth/confirmations_controller.rb","lineNumber":32,"sourceCode":"        if signed_in?(resource_name)\n          token = signed_in_resource.create_token\n          signed_in_resource.save!\n\n          redirect_headers = build_redirect_headers(token.token,\n                                                    token.client,\n                                                    redirect_header_options)\n\n          redirect_to_link = signed_in_resource.build_auth_url(redirect_url, redirect_headers)\n        else\n          redirect_to_link = DeviseTokenAuth::Url.generate(redirect_url, redirect_header_options)\n        end\n\n        redirect_to(redirect_to_link, redirect_options)\n      else\n        if redirect_url\n          redirect_to DeviseTokenAuth::Url.generate(redirect_url, account_confirmation_success: false), redirect_options\n        else\n          raise ActionController::RoutingError, 'Not Found'\n        end\n      end\n    end\n\n    def create\n      return render_create_error_missing_email if resource_params[:email].blank?\n\n      @email = get_case_insensitive_field_from_resource_params(:email)\n\n      @resource = resource_class.dta_find_by(uid: @email, provider: provider)\n\n      return render_not_found_error unless @resource\n\n      @resource.send_confirmation_instructions({\n        redirect_url: redirect_url,\n        client_config: resource_params[:config_name]\n      })\n","sourceCodeStart":14,"sourceCodeEnd":50,"githubUrl":"https://github.com/lynndylanhurley/devise_token_auth/blob/b02076a930f7d0a4a155e25feaa7a50b86dbc625/app/controllers/devise_token_auth/confirmations_controller.rb#L14-L50","documentation":"Raised by the GET /auth/confirmation endpoint (ConfirmationsController#show, confirmations_controller.rb:32). The action runs Devise's confirm_by_token; when the confirmation_token is blank, unknown, already consumed, or older than Devise.confirm_within, the resource comes back with errors. If no redirect_url is available (neither the redirect_url param nor the DeviseTokenAuth.default_confirm_success_url initializer setting), the controller raises ActionController::RoutingError so Rails renders its standard 404 page; with a redirect_url present it would instead redirect with account_confirmation_success=false. The raise is a deliberate dead-end for token-less API traffic, not a routing misconfiguration.","triggerScenarios":"GET /auth/confirmation?confirmation_token=X where X is expired (past Devise.confirm_within), already used (tokens are single-use and cleared after confirmation), truncated or HTML-escaped by a mail client, or missing entirely -- AND the request carries no redirect_url param AND DeviseTokenAuth.default_confirm_success_url is unset (the default).","commonSituations":"API or mobile clients that hit the confirmation endpoint directly without a redirect_url always receive the HTML 404 instead of JSON; users re-clicking an old confirmation link after the account is already confirmed; email links mangled by mail software; test suites reusing a consumed token; apps that never configured default_confirm_success_url in config/initializers/devise_token_auth.rb.","solutions":["Set config.default_confirm_success_url in config/initializers/devise_token_auth.rb (or always send a redirect_url param) so failed confirmations redirect with account_confirmation_success=false instead of raising 404","If the token is expired or already used, resend instructions via POST /auth/confirmation with the account email and use the link from the newest email","Verify the token arrived intact: compare it against User.find_by(email: ...).confirmation_token in the Rails console to catch truncation or escaping by the mail client","For API-only apps, override DeviseTokenAuth::ConfirmationsController#show in the host app to render a JSON error body instead of raising ActionController::RoutingError"],"exampleFix":"# before - config/initializers/devise_token_auth.rb\nDeviseTokenAuth.setup do |config|\n  # default_confirm_success_url never set: a bad token plus no redirect_url param raises RoutingError\nend\n\n# after\nDeviseTokenAuth.setup do |config|\n  config.default_confirm_success_url = 'https://app.example.com/auth/confirm-success'\nend","handlingStrategy":"validation","validationCode":"// before opening or forwarding the confirmation link\nfunction buildConfirmationUrl(baseUrl, token) {\n  if (!token || typeof token !== 'string') {\n    throw new Error('confirmation_token is missing or empty');\n  }\n  const url = new URL('/auth/confirmation', baseUrl);\n  url.searchParams.set('confirmation_token', token);\n  url.searchParams.set('redirect_url', 'https://app.example.com/auth/confirm-success');\n  return url.toString();\n}","typeGuard":"// Devise tokens are URL-safe base64-style strings; reject garbage before the request\nconst isPlausibleConfirmationToken = (t) =>\n  typeof t === 'string' && /^[A-Za-z0-9_-]{10,}$/.test(t);","tryCatchPattern":"// server-side caller (Rails proxy or request spec) around the endpoint\nbegin\n  get '/auth/confirmation', params: { confirmation_token: token, redirect_url: redirect }\nrescue ActionController::RoutingError\n  # invalid, expired, or consumed token: resend instructions, never retry the same token\nend","preventionTips":["Always include a whitelisted redirect_url when generating or forwarding confirmation links","Set DeviseTokenAuth.default_confirm_success_url so token failures degrade to a redirect with account_confirmation_success=false instead of a 404","Treat confirmation links as single-use: never prefetch them with link previews or fire them twice","Requesting new instructions regenerates the token: always start from the most recent confirmation email"],"tags":["devise-token-auth","rails","confirmation-token","account-confirmation","http-404","authentication"],"backgroundTag":"invalid-or-expired-verification-token","analyzedSha":"b02076a930f7d0a4a155e25feaa7a50b86dbc625","analyzedAt":"2026-08-23T09:41:47.905Z","schemaVersion":2},"datasetVersion":"2026-08-23T13:39:53.451Z"}