Skip to content

fix: The set_project method on line 862-864 queries for a pr... - #151

Merged
alexshapalov merged 2 commits into
mainfrom
ai-fix/project-not-found-performance-index
Apr 20, 2026
Merged

alexshapalov merged 2 commits into
mainfrom
ai-fix/project-not-found-performance-index

Conversation

@alexshapalov

Copy link
Copy Markdown
Contributor

🐛 Bug Fix: ActiveRecord::RecordNotFound

Issue ID: #2514
Controller: PerformanceController#index
Occurrences: 2 times
First seen: 2026-04-20 17:09
Last seen: 2026-04-20 17:09

🔍 Root Cause Analysis

The set_project method on line 862-864 queries for a project by id without scoping to the current account. However, the error message shows the query IS scoped to account_id, which means the issue is that project ID 30 doesn't exist in the current user's account. The user is attempting to access a project they don't have permission to view, or the project was deleted.

🔧 Suggested Fix

File: app/controllers/performance_controller.rb

Line: 862

Before:

def set_project
  @project = current_account.projects.find(params[:project_id])
end

After:

def set_project
  @project = current_account.projects.find_by(id: params[:project_id])
  redirect_to performance_path, alert: "Project not found or access denied" if @project.blank?
end

📋 Error Details

Error Message:

Couldn't find Project with 'id'="30" [WHERE "projects"."account_id" = $1]

Stack Trace (top frames):

["/usr/local/bundle/ruby/3.4.0/gems/activerecord-8.0.2.1/lib/active_record/relation/finder_methods.rb:429:in 'ActiveRecord::FinderMethods#raise_record_not_found_exception!'", "/usr/local/bundle/ruby/3.4.0/gems/activerecord-8.0.2.1/lib/active_record/relation/finder_methods.rb:537:in 'ActiveRecord::FinderMethods#find_one'", "/usr/local/bundle/ruby/3.4.0/gems/activerecord-8.0.2.1/lib/active_record/relation/finder_methods.rb:514:in 'ActiveRecord::FinderMethods#find_with_ids'", "/usr/local/bundle/ruby/3.4.0/gems/activerecord-8.0.2.1/lib/active_record/relation/finder_methods.rb:100:in 'ActiveRecord::FinderMethods#find'", "/usr/local/bundle/ruby/3.4.0/gems/activerecord-8.0.2.1/lib/active_record/associations/collection_association.rb:113:in 'ActiveRecord::Associations::CollectionAssociation#find'", "/usr/local/bundle/ruby/3.4.0/gems/activerecord-8.0.2.1/lib/active_record/associations/collection_proxy.rb:140:in 'ActiveRecord::Associations::CollectionProxy#find'", "/rails/app/controllers/performance_controller.rb:822:in 'PerformanceController#set_project'", "/usr/local/bundle/ruby/3.4.0/gems/activesupport-8.0.2.1/lib/active_support/callbacks.rb:361:in 'block in ActiveSupport::Callbacks::CallTemplate::MethodCall#make_lambda'", "/usr/local/bundle/ruby/3.4.0/gems/activesupport-8.0.2.1/lib/active_support/callbacks.rb:178:in 'block in ActiveSupport::Callbacks::Filters::Before#call'", "/usr/local/bundle/ruby/3.4.0/gems/actionpack-8.0.2.1/lib/abstract_controller/callbacks.rb:34:in 'block (2 levels) in <module:Callbacks>'", "/usr/local/bundle/ruby/3.4.0/gems/activesupport-8.0.2.1/lib/active_support/callbacks.rb:179:in 'ActiveSupport::Callbacks::Filters::Before#call'", "/usr/local/bundle/ruby/3.4.0/gems/activesupport-8.0.2.1/lib/active_support/callbacks.rb:559:in 'block in ActiveSupport::Callbacks::CallbackSequence#invoke_before'", "/usr/local/bundle/ruby/3.4.0/gems/activesupport-8.0.2.1/lib/active_support/callbacks.rb:559:in 'Array#each'", "/usr/local/bundle/ruby/3.4.0/gems/activesupport-8.0.2.1/lib/active_support/callbacks.rb:559:in 'ActiveSupport::Callbacks::CallbackSequence#invoke_before'", "/usr/local/bundle/ruby/3.4.0/gems/activesupport-8.0.2.1/lib/active_support/callbacks.rb:118:in 'block in ActiveSupport::Callbacks#run_callbacks'", "/usr/local/bundle/ruby/3.4.0/gems/turbo-rails-2.0.16/lib/turbo-rails.rb:24:in 'Turbo.with_request_id'", "/usr/local/bundle/ruby/3.4.0/gems/turbo-rails-2.0.16/app/controllers/concerns/turbo/request_id_tracking.rb:10:in 'Turbo::RequestIdTracking#turbo_tracking_request_id'", "/usr/local/bundle/ruby/3.4.0/gems/activesupport-8.0.2.1/lib/active_support/callbacks.rb:129:in 'block in ActiveSupport::Callbacks#run_callbacks'", "/usr/local/bundle/ruby/3.4.0/gems/actiontext-8.0.2.1/lib/action_text/rendering.rb:25:in 'ActionText::Rendering::ClassMethods#with_renderer'", "/usr/local/bundle/ruby/3.4.0/gems/actiontext-8.0.2.1/lib/action_text/engine.rb:71:in 'block (4 levels) in <class:Engine>'", "/usr/local/bundle/ruby/3.4.0/gems/activesupport-8.0.2.1/lib/active_support/callbacks.rb:129:in 'BasicObject#instance_exec'", "/usr/local/bundle/ruby/3.4.0/gems/activesupport-8.0.2.1/lib/active_support/callbacks.rb:129:in 'block in ActiveSupport::Callbacks#run_callbacks'", "/usr/local/bundle/ruby/3.4.0/gems/activesupport-8.0.2.1/lib/active_support/callbacks.rb:140:in 'ActiveSupport::Callbacks#run_callbacks'", "/usr/local/bundle/ruby/3.4.0/gems/actionpack-8.0.2.1/lib/abstract_controller/callbacks.rb:260:in 'AbstractController::Callbacks#process_action'", "/usr/local/bundle/ruby/3.4.0/gems/actionpack-8.0.2.1/lib/action_controller/metal/rescue.rb:27:in 'ActionController::Rescue#process_action'", "/usr/local/bundle/ruby/3.4.0/gems/actionpack-8.0.2.1/lib/action_controller/metal/instrumentation.rb:76:in 'block in ActionController::Instrumentation#process_action'", "/usr/local/bundle/ruby/3.4.0/gems/activesupport-8.0.2.1/lib/active_support/notifications.rb:210:in 'block in ActiveSupport::Notifications.instrument'", "/usr/local/bundle/ruby/3.4.0/gems/activesupport-8.0.2.1/lib/active_support/notifications/instrumenter.rb:58:in 'ActiveSupport::Notifications::Instrumenter#instrument'", "/usr/local/bundle/ruby/3.4.0/gems/activesupport-8.0.2.1/lib/active_support/notifications.rb:210:in 'ActiveSupport::Notifications.instrument'", "/usr/local/bundle/ruby/3.4.0/gems/actionpack-8.0.2.1/lib/action_controller/metal/instrumentation.rb:75:in 'ActionController::Instrumentation#process_action'", "/usr/local/bundle/ruby/3.4.0/gems/actionpack-8.0.2.1/lib/action_controller/metal/params_wrapper.rb:259:in 'ActionController::ParamsWrapper#process_action'", "/usr/local/bundle/ruby/3.4.0/gems/activerecord-8.0.2.1/lib/active_record/railties/controller_runtime.rb:39:in 'ActiveRecord::Railties::ControllerRuntime#process_action'", "/usr/local/bundle/ruby/3.4.0/gems/actionpack-8.0.2.1/lib/abstract_controller/base.rb:163:in 'AbstractController::Base#process'", "/usr/local/bundle/ruby/3.4.0/gems/actionview-8.0.2.1/lib/action_view/rendering.rb:40:in 'ActionView::Rendering#process'", "/usr/local/bundle/ruby/3.4.0/gems/actionpack-8.0.2.1/lib/action_controller/metal.rb:252:in 'ActionController::Metal#dispatch'", "/usr/local/bundle/ruby/3.4.0/gems/actionpack-8.0.2.1/lib/action_controller/metal.rb:335:in 'ActionController::Metal.dispatch'", "/usr/local/bundle/ruby/3.4.0/gems/actionpack-8.0.2.1/lib/action_dispatch/routing/route_set.rb:67:in 'ActionDispatch::Routing::RouteSet::Dispatcher#dispatch'", "/usr/local/bundle/ruby/3.4.0/gems/actionpack-8.0.2.1/lib/action_dispatch/routing/route_set.rb:50:in 'ActionDispatch::Routing::RouteSet::Dispatcher#serve'", "/usr/local/bundle/ruby/3.4.0/gems/actionpack-8.0.2.1/lib/action_dispatch/journey/router.rb:53:in 'block in ActionDispatch::Journey::Router#serve'", "/usr/local/bundle/ruby/3.4.0/gems/actionpack-8.0.2.1/lib/action_dispatch/journey/router.rb:133:in 'block in ActionDispatch::Journey::Router#find_routes'", "/usr/local/bundle/ruby/3.4.0/gems/actionpack-8.0.2.1/lib/action_dispatch/journey/router.rb:126:in 'Array#each'", "/usr/local/bundle/ruby/3.4.0/gems/actionpack-8.0.2.1/lib/action_dispatch/journey/router.rb:126:in 'ActionDispatch::Journey::Router#find_routes'", "/usr/local/bundle/ruby/3.4.0/gems/actionpack-8.0.2.1/lib/action_dispatch/journey/router.rb:34:in 'ActionDispatch::Journey::Router#serve'", "/usr/local/bundle/ruby/3.4.0/gems/actionpack-8.0.2.1/lib/action_dispatch/routing/route_set.rb:908:in 'ActionDispatch::Routing::RouteSet#call'", "/usr/local/bundle/ruby/3.4.0/gems/omniauth-2.1.4/lib/omniauth/strategy.rb:202:in 'OmniAuth::Strategy#call!'", "/usr/local/bundle/ruby/3.4.0/gems/omniauth-2.1.4/lib/omniauth/strategy.rb:169:in 'OmniAuth::Strategy#call'", "/usr/local/bundle/ruby/3.4.0/gems/omniauth-2.1.4/lib/omniauth/strategy.rb:202:in 'OmniAuth::Strategy#call!'", "/usr/local/bundle/ruby/3.4.0/gems/omniauth-2.1.4/lib/omniauth/strategy.rb:169:in 'OmniAuth::Strategy#call'", "/usr/local/bundle/ruby/3.4.0/gems/rack-attack-6.7.0/lib/rack/attack.rb:103:in 'Rack::Attack#call'", "/usr/local/bundle/ruby/3.4.0/gems/rack-attack-6.7.0/lib/rack/attack.rb:127:in 'Rack::Attack#call'", "/usr/local/bundle/ruby/3.4.0/gems/warden-1.2.9/lib/warden/manager.rb:36:in 'block in Warden::Manager#call'", "/usr/local/bundle/ruby/3.4.0/gems/warden-1.2.9/lib/warden/manager.rb:34:in 'Kernel#catch'", "/usr/local/bundle/ruby/3.4.0/gems/warden-1.2.9/lib/warden/manager.rb:34:in 'Warden::Manager#call'", "/usr/local/bundle/ruby/3.4.0/gems/rack-3.2.0/lib/rack/tempfile_reaper.rb:20:in 'Rack::TempfileReaper#call'", "/usr/local/bundle/ruby/3.4.0/gems/rack-3.2.0/lib/rack/etag.rb:29:in 'Rack::ETag#call'", "/usr/local/bundle/ruby/3.4.0/gems/rack-3.2.0/lib/rack/conditional_get.rb:31:in 'Rack::ConditionalGet#call'", "/usr/local/bundle/ruby/3.4.0/gems/rack-3.2.0/lib/rack/head.rb:15:in 'Rack::Head#call'", "/usr/local/bundle/ruby/3.4.0/gems/actionpack-8.0.2.1/lib/action_dispatch/http/permissions_policy.rb:38:in 'ActionDispatch::PermissionsPolicy::Middleware#call'", "/usr/local/bundle/ruby/3.4.0/gems/actionpack-8.0.2.1/lib/action_dispatch/http/content_security_policy.rb:38:in 'ActionDispatch::ContentSecurityPolicy::Middleware#call'", "/usr/local/bundle/ruby/3.4.0/gems/rack-session-2.1.1/lib/rack/session/abstract/id.rb:274:in 'Rack::Session::Abstract::Persisted#context'", "/usr/local/bundle/ruby/3.4.0/gems/rack-session-2.1.1/lib/rack/session/abstract/id.rb:268:in 'Rack::Session::Abstract::Persisted#call'", "/usr/local/bundle/ruby/3.4.0/gems/actionpack-8.0.2.1/lib/action_dispatch/middleware/cookies.rb:706:in 'ActionDispatch::Cookies#call'", "/usr/local/bundle/ruby/3.4.0/gems/actionpack-8.0.2.1/lib/action_dispatch/middleware/callbacks.rb:31:in 'block in ActionDispatch::Callbacks#call'", "/usr/local/bundle/ruby/3.4.0/gems/activesupport-8.0.2.1/lib/active_support/callbacks.rb:100:in 'ActiveSupport::Callbacks#run_callbacks'", "/usr/local/bundle/ruby/3.4.0/gems/actionpack-8.0.2.1/lib/action_dispatch/middleware/callbacks.rb:30:in 'ActionDispatch::Callbacks#call'", "/usr/local/bundle/ruby/3.4.0/gems/activerabbit-ai-0.6.4/lib/active_rabbit/middleware/error_capture_middleware.rb:11:in 'ActiveRabbit::Middleware::ErrorCaptureMiddleware#call'", "/usr/local/bundle/ruby/3.4.0/gems/actionpack-8.0.2.1/lib/action_dispatch/middleware/debug_exceptions.rb:31:in 'ActionDispatch::DebugExceptions#call'", "/usr/local/bundle/ruby/3.4.0/gems/actionpack-8.0.2.1/lib/action_dispatch/middleware/show_exceptions.rb:32:in 'ActionDispatch::ShowExceptions#call'", "/usr/local/bundle/ruby/3.4.0/gems/railties-8.0.2.1/lib/rails/rack/logger.rb:41:in 'Rails::Rack::Logger#call_app'", "/usr/local/bundle/ruby/3.4.0/gems/railties-8.0.2.1/lib/rails/rack/logger.rb:29:in 'Rails::Rack::Logger#call'", "/usr/local/bundle/ruby/3.4.0/gems/railties-8.0.2.1/lib/rails/rack/silence_request.rb:28:in 'Rails::Rack::SilenceRequest#call'", "/usr/local/bundle/ruby/3.4.0/gems/actionpack-8.0.2.1/lib/action_dispatch/middleware/remote_ip.rb:96:in 'ActionDispatch::RemoteIp#call'", "/usr/local/bundle/ruby/3.4.0/gems/actionpack-8.0.2.1/lib/action_dispatch/middleware/request_id.rb:34:in 'ActionDispatch::RequestId#call'", "/usr/local/bundle/ruby/3.4.0/gems/rack-3.2.0/lib/rack/method_override.rb:28:in 'Rack::MethodOverride#call'", "/usr/local/bundle/ruby/3.4.0/gems/rack-3.2.0/lib/rack/runtime.rb:24:in 'Rack::Runtime#call'", "/usr/local/bundle/ruby/3.4.0/gems/activesupport-8.0.2.1/lib/active_support/cache/strategy/local_cache_middleware.rb:29:in 'ActiveSupport::Cache::Strategy::LocalCache::Middleware#call'", "/usr/local/bundle/ruby/3.4.0/gems/actionpack-8.0.2.1/lib/action_dispatch/middleware/executor.rb:16:in 'ActionDispatch::Executor#call'", "/usr/local/bundle/ruby/3.4.0/gems/actionpack-8.0.2.1/lib/action_dispatch/middleware/static.rb:27:in 'ActionDispatch::Static#call'", "/usr/local/bundle/ruby/3.4.0/gems/rack-3.2.0/lib/rack/sendfile.rb:114:in 'Rack::Sendfile#call'", "/usr/local/bundle/ruby/3.4.0/gems/actionpack-8.0.2.1/lib/action_dispatch/middleware/assume_ssl.rb:24:in 'ActionDispatch::AssumeSSL#call'", "/usr/local/bundle/ruby/3.4.0/gems/rack-cors-3.0.0/lib/rack/cors.rb:102:in 'Rack::Cors#call'", "/usr/local/bundle/ruby/3.4.0/gems/railties-8.0.2.1/lib/rails/engine.rb:535:in 'Rails::Engine#call'", "/usr/local/bundle/ruby/3.4.0/gems/puma-6.6.1/lib/puma/configuration.rb:279:in 'Puma::Configuration::ConfigMiddleware#call'", "/usr/local/bundle/ruby/3.4.0/gems/puma-6.6.1/lib/puma/request.rb:99:in 'block in Puma::Request#handle_request'", "/usr/local/bundle/ruby/3.4.0/gems/puma-6.6.1/lib/puma/thread_pool.rb:390:in 'Puma::ThreadPool#with_force_shutdown'", "/usr/local/bundle/ruby/3.4.0/gems/puma-6.6.1/lib/puma/request.rb:98:in 'Puma::Request#handle_request'", "/usr/local/bundle/ruby/3.4.0/gems/puma-6.6.1/lib/puma/server.rb:472:in 'Puma::Server#process_client'", "/usr/local/bundle/ruby/3.4.0/gems/puma-6.6.1/lib/puma/server.rb:254:in 'block in Puma::Server#run'", "/usr/local/bundle/ruby/3.4.0/gems/puma-6.6.1/lib/puma/thread_pool.rb:167:in 'block in Puma::ThreadPool#spawn_thread'"]

Request Context:

  • Method: GET
  • Path: /projects/30/performance?sort=status_desc

🛡️ Prevention

  • Use find_by instead of find to avoid raising RecordNotFound exceptions; this allows graceful error handling.
  • Always redirect to a safe fallback path (like performance_path) when a requested resource doesn't exist in the user's scope, rather than letting the exception propagate.
  • Consider adding a rescue_from ActiveRecord::RecordNotFound handler at the controller level if you want centralized error handling across all actions.

✅ Checklist

  • Code fix implemented
  • Tests added/updated
  • Error scenario manually verified
  • No regressions introduced

Generated by ActiveRabbit AI

fix: app/controllers/performance_controller.rb
@alexshapalov
alexshapalov marked this pull request as ready for review April 20, 2026 21:44
@alexshapalov
alexshapalov merged commit 4e2c373 into main Apr 20, 2026
3 of 4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant