instructure/canvas-lms · error · Lti::LocalAppNotFound

No local App/Lti::Registration found for registration_id=#

Error message

No local App/Lti::Registration found for registration_id=#{old_registration_id}

What it means

Lti::ContextControl#sync_app_id tries to resolve an old registration_id to a local Lti::Registration/App. When no local record is found, behavior depends on the root account feature flag lti_registrations_templates: if enabled it raises Lti::LocalAppNotFound; otherwise it logs a warning via Canvas::Errors and continues.

Solutions

  1. Create/backfill the missing local Lti::Registration matching old_registration_id before syncing
  2. Verify old_registration_id points to a registration that exists on the same shard
  3. If legacy data is expected, disable the lti_registrations_templates feature on the root account so sync degrades to a warning instead of raising
  4. Rescue Lti::LocalAppNotFound at the sync call site and handle the missing-app case

Example fix

// before
control.update!(app_id: old_registration_id) # raises when flag on
// after
begin
  control.sync_app_id
rescue Lti::LocalAppNotFound => e
  Canvas::Errors.capture_exception(:lti_registration_sync, e, :warn)
end
Defensive patterns

Strategy: try-catch

Validate before calling

unless Lti::Registration.where(id: old_registration_id).exists? then handle_missing_app end

Type guard

def local_app_present?(id) = Lti::Registration.exists?(id)

Try / catch

begin
  context_control.sync_app_id
rescue Lti::LocalAppNotFound => e
  Canvas::Errors.capture_exception(:lti_registration_sync, e, :warn)
end

Prevention

When it happens

Trigger: Syncing a ContextControl whose stored registration_id does not correspond to any local Lti::Registration/App, while the resolved root account has the lti_registrations_templates feature enabled.

Common situations: Data migrated or imported from another shard/environment where the registration does not exist locally; stale registration_id after deleting a registration; accounts with the feature flag on but incomplete registration backfill.

Understand the failure class

Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.

Related errors


AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15). Data as JSON: /api/errors/73799a510420d5e2. Report an issue: GitHub.

Appendix: source

Thrown at app/models/lti/context_control.rb:367

    resolved_root_account = deployment&.root_account
    cross_shard = shard != Shard.shard_for(old_registration_id)
    same_shard_inherited = !cross_shard &&
                           resolved_root_account != Account.site_admin &&
                           shard == Account.site_admin.shard &&
                           old_registration&.root_account == Account.site_admin

    if cross_shard || same_shard_inherited
      local = Lti::Registration.active.find_by(
        template_registration_id: old_registration_id,
        account: resolved_root_account
      )
      if local
        self.app_id = local.id
      else
        msg = "No local App/Lti::Registration found for registration_id=#{old_registration_id}"
        if resolved_root_account&.feature_enabled?(:lti_registrations_templates)
          raise Lti::LocalAppNotFound, msg
        else
          Canvas::Errors.capture_exception(:lti_registration_sync, msg, :warn)
        end
      end
    else
      self.app_id = old_registration_id
    end
  end

  def context_belongs_to_deployment
    return unless (account_id || course_id) && deployment

    if deployment.context_type == "Course"
      # context can only be the course itself
      unless course_id == deployment.context_id
        errors.add(:context, I18n.t("must be the deployment's context"))
      end
    else

View on GitHub (pinned to 1c9f0bb801)