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

No local App/Lti::Registration found for…

Error message

No local App/Lti::Registration found for lti_registration_id=#{old_lti_registration_id}

What it means

Lti::RegistrationHistoryEntry#sync_app_id resolves an Lti::Registration to its local App record; when no local App matches old_lti_registration_id the behavior depends on the root account's :lti_registrations_templates feature flag. If enabled, it raises Lti::LocalAppNotFound with this message; otherwise it only logs a warning via Canvas::Errors.capture_exception.

Solutions

  1. Verify a local App exists for the given lti_registration_id (create/backfill the App record) before syncing history.
  2. Confirm the registration belongs to the same root account/shard; switch to the correct shard when resolving.
  3. If the registration was legitimately deleted, clean up or skip the stale history entry instead of syncing.
  4. If the strict raise is undesirable, disable the :lti_registrations_templates feature flag on the root account so it degrades to a warning.

Example fix

// before
entry.sync_app_id  # raises when App is missing and flag enabled
// after
if Lti::Registration.where(id: entry.old_lti_registration_id).joins(:app).exists?
  entry.sync_app_id
else
  Canvas::Errors.capture_exception(:lti_registration_sync, "missing app for #{entry.old_lti_registration_id}", :warn)
end
Defensive patterns

Strategy: try-catch

Validate before calling

app = App.where(lti_registration_id: entry.old_lti_registration_id).first
entry.sync_app_id only if app.present?

Try / catch

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

Prevention

When it happens

Trigger: Syncing registration history where old_lti_registration_id points to a registration that has no corresponding local App row — typically after the registration was deleted, is from a different shard/root account, or the App/registration mapping was never created.

Common situations: Cross-shard registration references; accounts that enabled lti_registrations_templates after stale history rows existed; registrations deleted during cleanup while their history entries were later synced.

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/bc800d15b02a584d. Report an issue: GitHub.

Appendix: source

Thrown at app/models/lti/registration_history_entry.rb:395

    return if app_id_changed? && !app_id.nil?

    cross_shard = shard != Shard.shard_for(old_lti_registration_id)
    same_shard_inherited = !cross_shard &&
                           root_account != Account.site_admin &&
                           shard == Account.site_admin.shard &&
                           old_lti_registration&.root_account == Account.site_admin

    if cross_shard || same_shard_inherited
      local = Lti::Registration.active.find_by(
        template_registration_id: old_lti_registration_id,
        account: root_account
      )
      if local
        self.app_id = local.id
      else
        msg = "No local App/Lti::Registration found for lti_registration_id=#{old_lti_registration_id}"
        if 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_lti_registration_id
    end
  end

  def valid_columns_for_update_type
    case update_type
    when "control_edit"
      if old_configuration.present? || new_configuration.present?
        errors.add("old_config", "Cannot modify config when updating tool availability")
      end
    when "manual_edit" || "registration_update"
      if old_context_controls.present? || new_context_controls.present?
        errors.add("old_context_controls", "Cannot modify context control attributes when updating tool availability")

View on GitHub (pinned to 1c9f0bb801)