{"record":{"id":"9fb9d79974babdba","repo":"instructure/canvas-lms","slug":"unable-to-find-discussion-topic","errorCode":null,"errorMessage":"Unable to find Discussion Topic","messagePattern":"Unable to find Discussion Topic","errorType":"exception","errorClass":"GraphQL::ExecutionError","httpStatus":null,"severity":"error","filePath":"app/graphql/mutations/delete_discussion_topic.rb","lineNumber":30,"sourceCode":"# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY\n# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more\n# details.\n#\n# You should have received a copy of the GNU Affero General Public License along\n# with this program. If not, see <http://www.gnu.org/licenses/>.\n#\nclass Mutations::DeleteDiscussionTopic < Mutations::BaseMutation\n  graphql_name \"DeleteDiscussionTopic\"\n  # input arguments\n  argument :id, ID, required: true, prepare: GraphQLHelpers.relay_or_legacy_id_prepare_func(\"DiscussionTopic\")\n\n  # the return data if the delete is successful\n  field :discussion_topic_id, ID, null: false\n\n  def resolve(input:)\n    record = DiscussionTopic.active.find_by(id: input[:id])\n    raise GraphQL::ExecutionError, \"Unable to find Discussion Topic\" if record.nil? || !record.grants_right?(current_user, nil, :read)\n\n    unless record.grants_right?(current_user, nil, :delete)\n      raise GraphQL::ExecutionError, \"Insufficient permissions\"\n    end\n\n    context[:deleted_models] = { discussion_topic: {} }\n    context[:deleted_models][:discussion_topic] = record\n    record.destroy\n    {\n      discussion_topic_id: record.id\n    }\n  end\n\n  def self.discussion_topic_id_log_entry(_topic, context)\n    context[:deleted_models][:discussion_topic]\n  end\nend\n","sourceCodeStart":12,"sourceCodeEnd":48,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/graphql/mutations/delete_discussion_topic.rb#L12-L48","documentation":"GraphQL::ExecutionError raised in Mutations::DeleteDiscussionTopic#resolve when DiscussionTopic.active.find_by(id: input[:id]) returns nil or the record fails the :read rights check. Like delete_discussion_entry, authorization failures are masked as 'Unable to find Discussion Topic' to avoid revealing record existence.","triggerScenarios":"Deleting a discussion topic with a nonexistent id, an id of an already-deleted (non-active) topic, or a topic the user cannot read (no enrollment/permissions) — all collapse into this single message.","commonSituations":"Stale topic ids in the UI after a topic was deleted by another teacher; students attempting to delete topics they cannot read; id encoding mismatches between REST and GraphQL surfaces.","solutions":["Confirm the topic exists and is active: DiscussionTopic.active.exists?(id)","Verify the caller has :read and :delete rights on the topic's course","Refresh the client's topic list instead of retrying with cached ids","Call as an admin (or with appropriate course permissions) for moderation deletes"],"exampleFix":"// before\ndeleteDiscussionTopic(id) // topic deleted elsewhere -> 'Unable to find Discussion Topic'\n// after\nconst topic = topics.find(t => t.id === id)\nif (topic) await deleteDiscussionTopic(id)\nelse refreshTopics()","handlingStrategy":"try-catch","validationCode":"const exists = topics.some(t => t.id === id) && topic.grantsRight(currentUser, 'delete')\nif (!exists) skipDelete()","typeGuard":null,"tryCatchPattern":"try {\n  await deleteDiscussionTopic(id)\n} catch (e) {\n  if (/Unable to find Discussion Topic/.test(e.message)) refreshTopics()\n}","preventionTips":["Invalidate cached topic lists after external deletions","Require explicit read+delete rights before calling","Distinguish nonexistent ids from permission failures when debugging"],"tags":["graphql","not-found","discussion"],"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"}