lynndylanhurley/devise_token_auth · error · ActionController::RoutingError

Not Found

Error message

Not Found

What it means

Raised by the GET /auth/unlock endpoint (UnlocksController#show, the landing action for account-unlock emails; render_show_error at unlocks_controller.rb:79). It applies when Devise lockable runs with an email unlock strategy. The action calls unlock_access_by_token; when no persisted user matches the unlock_token (blank, unknown, or already consumed because unlocking clears the stored token), render_show_error raises ActionController::RoutingError and Rails returns 404, deliberately hiding whether the account exists.

Source

Thrown at app/controllers/devise_token_auth/unlocks_controller.rb:79

      render_error(401, I18n.t('devise_token_auth.unlocks.missing_email'))
    end

    def render_create_success
      render json: {
        success: true,
        message: success_message('unlocks', @email)
      }
    end

    def render_create_error(errors)
      render json: {
        success: false,
        errors: errors
      }, status: 400
    end

    def render_show_error
      raise ActionController::RoutingError, 'Not Found'
    end

    def render_not_found_error
      if Devise.paranoid
        render_create_success
      else
        render_error(404, I18n.t('devise_token_auth.unlocks.user_not_found', email: @email))
      end
    end

    def resource_params
      params.permit(:email, :unlock_token, :config)
    end
  end
end

View on GitHub (pinned to b02076a930)

Solutions

  1. If the account is still locked, request a new unlock email via POST /auth/unlock with the account email and use the newest link
  2. Verify the model declares devise :lockable with an unlock_strategy that includes :email and that the users table has the lockable columns (failed_attempts, unlock_token, locked_at) via the Devise lockable migration
  3. Compare the token in the link against User.find_by(email: ...).unlock_token in the console to catch truncation or escaping
  4. Unblock the user immediately from the console with user.unlock_access! or wait for the lock to lapse per Devise.unlock_in
  5. For API-only apps, override DeviseTokenAuth::UnlocksController#render_show_error to render JSON instead of raising RoutingError

Example fix

# before - devise_token_auth default
def render_show_error
  raise ActionController::RoutingError, 'Not Found'
end

# after - app/controllers/unlocks_controller.rb (host app override)
class UnlocksController < DeviseTokenAuth::UnlocksController
  def render_show_error
    render json: { success: false, errors: ['Invalid or already used unlock token.'] }, status: :not_found
  end
end

# config/routes.rb
mount_devise_token_auth_for 'User', at: 'auth', controllers: { unlocks: 'unlocks' }
Defensive patterns

Strategy: fallback

Validate before calling

// before sending the user to the unlock landing page
function assertUnlockLinkParams(query) {
  if (!query.get('unlock_token')) throw new Error('unlock_token missing from link');
  return query;
}

Type guard

const isPlausibleUnlockToken = (t) =>
  typeof t === 'string' && /^[A-Za-z0-9_-]{10,}$/.test(t);

Try / catch

// client handling of the unlock landing request
const res = await fetch(unlockUrl, { redirect: 'manual' });
if (res.status === 404) {
  // token unknown or already used: fall back to requesting a fresh unlock email
  await requestUnlockEmail(email);
}

Prevention

When it happens

Trigger: GET /auth/unlock?unlock_token=X where X is missing, matches no user row, was already used on an earlier visit (the token is cleared once the account is unlocked), or comes from an older unlock email after the account relocked and generated a newer token.

Common situations: Models missing devise :lockable or an unlock_strategy that includes :email, so no valid unlock tokens ever exist (or the lockable migration with unlock_token was never run); users clicking the unlock link twice (first click succeeds, second 404s); admins manually unlocking accounts while users later open the stale email; mail clients mangling the token in the link.

Related errors


AI-assisted analysis of lynndylanhurley/devise_token_auth@b02076a930 (2026-08-23). Data as JSON: /api/errors/8991a0e25f9ae86b. Report an issue: GitHub.