NoMethodError in DesignManagement::Repository#full_path when project is nil during Geo replication

Summary

A NoMethodError is raised in DesignManagement::Repository#full_path when the associated project is nil. This occurs during Geo replication when a DesignManagement::Repository record exists but its associated project has been deleted, leaving an orphaned record.

Sentry: https://new-sentry.gitlab.net/organizations/gitlab/issues/3543888/events/657880922a6e4253aae1fe6acbaf3cca/

Error

NoMethodError: undefined method `full_path' for nil (NoMethodError)

      project.full_path + repo_type.path_suffix
             ^^^^^^^^^^
  from app/models/design_management/repository.rb:26:in `full_path'

Call chain

  1. Geo::EventWorker#perform picks up a Geo replication event (created/updated).
  2. Geo::DesignManagementRepositoryReplicator#repository calls model_record.repository.
  3. DesignManagement::Repository#repository calls #full_path.
  4. DesignManagement::Repository#full_path executes project.full_path + repo_type.path_suffix, but project is nil.

The with_record_not_found_handling wrapper in Gitlab::Geo::Replicator only rescues ActiveRecord::RecordNotFound, so the NoMethodError escapes.

Root cause

A DesignManagement::Repository record exists in the database whose associated project has been deleted (orphaned record). The Geo secondary node received a replication event for this repository, but when it tries to resolve the record, the project is gone.

Proposed fix

Add a nil guard for project in both full_path and disk_path methods in app/models/design_management/repository.rb, raising ActiveRecord::RecordNotFound instead of letting a NoMethodError bubble up:

def full_path
  raise ActiveRecord::RecordNotFound, "Project not found for DesignManagement::Repository ##{id}" unless project

  project.full_path + repo_type.path_suffix
end

def disk_path
  raise ActiveRecord::RecordNotFound, "Project not found for DesignManagement::Repository ##{id}" unless project

  project.disk_path + repo_type.path_suffix
end

This approach:

  • Keeps the fix scoped to the model where the nil is encountered, rather than broadening the rescue in the shared Geo replicator.
  • The existing with_record_not_found_handling in Gitlab::Geo::Replicator will catch the ActiveRecord::RecordNotFound naturally.
  • The error message is descriptive about what wasn't found (the project, not the DesignManagement::Repository itself).
  • Non-Geo callers also get a clear error instead of a cryptic NoMethodError on nil.
Edited by 🤖 GitLab Bot 🤖