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
- Create/backfill the missing local Lti::Registration matching old_registration_id before syncing
- Verify old_registration_id points to a registration that exists on the same shard
- If legacy data is expected, disable the lti_registrations_templates feature on the root account so sync degrades to a warning instead of raising
- 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
- Backfill local registrations before enabling lti_registrations_templates
- Verify registration_ids exist on the same shard
- Rescue Lti::LocalAppNotFound at sync boundaries
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
- #
- Access token expired
- Access token invalid - signature likely incorrect
- assignment_invalid
- Assignment not configured for external tool launches
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
elseView on GitHub (pinned to 1c9f0bb801)