instructure/canvas-lms · error · ActiveRecord::RecordNotFound

ActiveRecord::RecordNotFound

Error message

ActiveRecord::RecordNotFound

What it means

restore_deleted_discussion_entry raises a raw ActiveRecord::RecordNotFound from DiscussionEntry.find(input[:discussion_entry_id]) (or the explicit raise unless the user can :read the entry). Rails' find raises when the id does not exist, including soft-deleted/shard-mismatched rows; the explicit raise hides deleted entries from users without read rights.

Solutions

  1. Confirm the entry id exists: DiscussionEntry.where(id: id).exists?
  2. Verify current_user has :read and :update rights on the entry's context.
  3. Look up the entry through its discussion_topic within the accessible course to get a clearer error.

Example fix

// before
entry = DiscussionEntry.find(input[:discussion_entry_id])
// after
entry = DiscussionEntry.where(id: input[:discussion_entry_id]).first
return validation_error('Entry not found or inaccessible') unless entry&.grants_right?(current_user, session, :read)
Defensive patterns

Strategy: try-catch

Validate before calling

// verify the entry is restorable and user has rights
const entry = await entryQuery(discussionEntryId)
if (!entry) return
if (!entry.viewerCanUpdate) showError('Insufficient Permissions')

Type guard

function isEntryAccessible(entry, user) { return !!entry && !entry.deleted && entry.permissions.read === true && entry.permissions.update === true }

Try / catch

try { await restoreDeletedDiscussionEntry({ id }) } catch (e) { if (e.message.includes('RecordNotFound')) showError('Entry no longer exists') else throw e }

Prevention

When it happens

Trigger: discussion_entry_id references a non-existent entry, an entry on a different shard, or an entry the current_user lacks :read rights on (e.g. not enrolled in the course).

Common situations: Clients restoring from a stale notifications/email link after the entry was hard-deleted; student attempting to restore another user's entry in a course they can no longer access.

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

Appendix: source

Thrown at app/graphql/mutations/restore_deleted_discussion_entry.rb:31

# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
#

class Mutations::RestoreDeletedDiscussionEntry < Mutations::BaseMutation
  argument :discussion_entry_id, ID, required: true, prepare: GraphQLHelpers.relay_or_legacy_id_prepare_func("DiscussionEntry")

  field :discussion_entry, Types::DiscussionEntryType, null: true

  def resolve(input:)
    entry = DiscussionEntry.find(input[:discussion_entry_id])

    return validation_error(I18n.t("Insufficient Permissions")) unless entry.context.feature_enabled?(:restore_discussion_entry)

    raise ActiveRecord::RecordNotFound unless entry.grants_right?(current_user, session, :read)
    return validation_error(I18n.t("Insufficient Permissions")) unless entry.grants_right?(current_user, session, :update)

    if entry.deleted?
      entry.saving_user = current_user
      entry.restore
      { discussion_entry: entry }
    else
      validation_error(I18n.t("Discussion entry is not deleted"))
    end
  end
end

View on GitHub (pinned to 1c9f0bb801)