{"record":{"id":"6f85655e5d37e7a9","repo":"instructure/canvas-lms","slug":"unable-to-determine-asset-type-for-lti-asset-id-id-the","errorCode":null,"errorMessage":"Unable to determine asset type for Lti::Asset id=#{id}. The referred discussion_entry_version or the submission has been probably deleted.","messagePattern":"Unable to determine asset type for Lti::Asset id=#(.+?)\\. The referred discussion_entry_version or the submission has been probably deleted\\.","errorType":"exception","errorClass":"UnknownTypeError","httpStatus":null,"severity":"error","filePath":"app/models/lti/asset.rb","lineNumber":120,"sourceCode":"      # In theory, submissions can be deleted, to not break the foreign key constraint we set the submission_id to null\n      # when the submission is hard deleted with dependent: :nullify in the Submission model.\n      return \"deleted\"\n    end\n\n    # if submission_attempt is set, it's RCE content\n    if submission_attempt.present?\n      TYPE_TEXT_ENTRY\n    # if attachment_id is set, it's a file attachment of a submission\n    elsif attachment_id.present?\n      TYPE_ATTACHMENT\n    # if discussion_entry_version_id is set, it's a discussion entry (comment)\n    elsif discussion_entry_version_id.present?\n      TYPE_DISCUSSION_ENTRY\n    else\n      Rails.logger.error(\n        \"Lti::Asset unknown type id=#{id}, submission_id=#{submission_id}, attachment_id=#{attachment_id}, submission_attempt=#{submission_attempt}, discussion_entry_version_id=#{discussion_entry_version_id}\"\n      )\n      raise UnknownTypeError, \"Unable to determine asset type for Lti::Asset id=#{id}. The referred discussion_entry_version or the submission has been probably deleted.\"\n    end\n  end\n\n  # Returns the submission claim ID for this asset.\n  # Discussion entries use DiscussionEntryVersion#lti_submission_claim_id.\n  # All other types delegate to Submission#lti_attempt_id with the asset's attempt number.\n  def submission_lti_claim_id\n    return nil unless submission\n\n    # Discussion Entry\n    if discussion_entry?\n      discussion_entry_version.lti_submission_claim_id(submission)\n    # Attachment of discussion entry\n    elsif attachment_id.present? && submission.submission_type == \"discussion_topic\"\n      # attachment is stored on discussion_entry not discussion_entry_version,\n      # so we find the most recent entry and version for the submission that contains the attachment\n      # and send that submission in the claim (also that is what we show in SG)\n      entry = DiscussionEntry.where(","sourceCodeStart":102,"sourceCodeEnd":138,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/models/lti/asset.rb#L102-L138","documentation":"Lti::Asset#asset_type infers the asset's type from whichever foreign key is present (submission, attachment, discussion_entry_version). If none of the referenced records' ids is set — typically because the underlying submission or discussion_entry_version was deleted — it logs the anomaly and raises UnknownTypeError, since an Lti::Asset with no resolvable backing record cannot be typed.","triggerScenarios":"Reading an Lti::Asset whose submission_id, attachment_id, and discussion_entry_version_id are all blank — e.g. after the Submission or DiscussionEntryVersion it pointed at was soft/hard deleted while the asset row (or a reference to it) persists.","commonSituations":"AGS/LTI score reporting for a submission deleted by a student or cleanup job; discussion topics/entries removed while LTI assets remain; speedgrader or analytics jobs touching stale assets.","solutions":["Purge or ignore Lti::Asset rows whose backing submission/discussion_entry_version no longer exists; guard callers with asset-aware existence checks.","Restore the deleted submission/discussion entry if the data should exist.","Wrap asset_type access in a rescue of Lti::Asset::UnknownTypeError and skip the asset.","Add cleanup callbacks so deleting a Submission/DiscussionEntryVersion also deletes dependent Lti::Asset rows."],"exampleFix":"// before\natype = asset.asset_type\n// after\natype = asset.asset_type\nrescue Lti::Asset::UnknownTypeError => e\n  Rails.logger.warn(\"Skipping stale Lti::Asset: #{e.message}\")\n  next","handlingStrategy":"try-catch","validationCode":"next if asset.submission_id.blank? && asset.attachment_id.blank? && asset.discussion_entry_version_id.blank?\natype = asset.asset_type","typeGuard":"def resolvable?(asset) = asset.submission_id.present? || asset.attachment_id.present? || asset.discussion_entry_version_id.present?","tryCatchPattern":"begin\n  atype = asset.asset_type\nrescue Lti::Asset::UnknownTypeError => e\n  Rails.logger.warn(\"Skipping stale asset: #{e.message}\")\n  next\nend","preventionTips":["Add dependent-destroy callbacks so Lti::Asset rows are removed with their Submission/DiscussionEntryVersion","Filter out assets whose backing records were deleted before processing batches","Monitor the Lti::Asset unknown-type log line as a data-integrity signal"],"tags":["lti","deleted-record","asset-type"],"backgroundTag":"entity-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"}