{"record":{"id":"bc800d15b02a584d","repo":"instructure/canvas-lms","slug":"no-local-app-lti-registration-found-for-lti-registration-id-bc800d","errorCode":null,"errorMessage":"No local App/Lti::Registration found for lti_registration_id=#{old_lti_registration_id}","messagePattern":"No local App/Lti::Registration found for lti_registration_id=#(.+?)","errorType":"exception","errorClass":"Lti::LocalAppNotFound","httpStatus":null,"severity":"error","filePath":"app/models/lti/registration_history_entry.rb","lineNumber":395,"sourceCode":"    return if app_id_changed? && !app_id.nil?\n\n    cross_shard = shard != Shard.shard_for(old_lti_registration_id)\n    same_shard_inherited = !cross_shard &&\n                           root_account != Account.site_admin &&\n                           shard == Account.site_admin.shard &&\n                           old_lti_registration&.root_account == Account.site_admin\n\n    if cross_shard || same_shard_inherited\n      local = Lti::Registration.active.find_by(\n        template_registration_id: old_lti_registration_id,\n        account: root_account\n      )\n      if local\n        self.app_id = local.id\n      else\n        msg = \"No local App/Lti::Registration found for lti_registration_id=#{old_lti_registration_id}\"\n        if root_account&.feature_enabled?(:lti_registrations_templates)\n          raise Lti::LocalAppNotFound, msg\n        else\n          Canvas::Errors.capture_exception(:lti_registration_sync, msg, :warn)\n        end\n      end\n    else\n      self.app_id = old_lti_registration_id\n    end\n  end\n\n  def valid_columns_for_update_type\n    case update_type\n    when \"control_edit\"\n      if old_configuration.present? || new_configuration.present?\n        errors.add(\"old_config\", \"Cannot modify config when updating tool availability\")\n      end\n    when \"manual_edit\" || \"registration_update\"\n      if old_context_controls.present? || new_context_controls.present?\n        errors.add(\"old_context_controls\", \"Cannot modify context control attributes when updating tool availability\")","sourceCodeStart":377,"sourceCodeEnd":413,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/models/lti/registration_history_entry.rb#L377-L413","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nentry.sync_app_id  # raises when App is missing and flag enabled\n// after\nif Lti::Registration.where(id: entry.old_lti_registration_id).joins(:app).exists?\n  entry.sync_app_id\nelse\n  Canvas::Errors.capture_exception(:lti_registration_sync, \"missing app for #{entry.old_lti_registration_id}\", :warn)\nend","handlingStrategy":"try-catch","validationCode":"app = App.where(lti_registration_id: entry.old_lti_registration_id).first\nentry.sync_app_id only if app.present?","typeGuard":null,"tryCatchPattern":"begin\n  entry.sync_app_id\nrescue Lti::LocalAppNotFound => e\n  Canvas::Errors.capture_exception(:lti_registration_sync, e, :warn)\nend","preventionTips":["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"],"tags":["ruby","lti","sync","record-missing"],"backgroundTag":"record-not-found","analyzedSha":"1c9f0bb8013ed69c4f2efe11fd483025469b7e6c","analyzedAt":"2026-09-15T20:33:18.891Z","contentChangedAt":"2026-09-15T20:33:18.891Z","schemaVersion":2},"datasetVersion":"2026-09-23T02:17:17.105Z"}