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
- Verify a local App exists for the given lti_registration_id (create/backfill the App record) before syncing history.
- Confirm the registration belongs to the same root account/shard; switch to the correct shard when resolving.
- If the registration was legitimately deleted, clean up or skip the stale history entry instead of syncing.
- 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
- Verify App exists for the registration on the same shard before syncing
- Clean up history entries when registrations are deleted
- Test cross-shard registration scenarios explicitly
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
- Assignment not configured for external tool launches
- Developer key mismatch
- e.message
- Missing required asset parameter #
- Missing required parameter: copied_at
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)