instructure/canvas-lms · error · GraphQL::ExecutionError
Unable to find Discussion Topic
Error message
Unable to find Discussion Topic
What it means
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.
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
Example fix
// before deleteDiscussionTopic(id) // topic deleted elsewhere -> 'Unable to find Discussion Topic' // after const topic = topics.find(t => t.id === id) if (topic) await deleteDiscussionTopic(id) else refreshTopics()
Defensive patterns
Strategy: try-catch
Validate before calling
const exists = topics.some(t => t.id === id) && topic.grantsRight(currentUser, 'delete') if (!exists) skipDelete()
Try / catch
try {
await deleteDiscussionTopic(id)
} catch (e) {
if (/Unable to find Discussion Topic/.test(e.message)) refreshTopics()
} Prevention
- Invalidate cached topic lists after external deletions
- Require explicit read+delete rights before calling
- Distinguish nonexistent ids from permission failures when debugging
When it happens
Trigger: 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.
Common situations: 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.
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
- Allocation rule not found
- Assignment not found
- assignment not found: #
- assignment not found
- custom grade status not found
AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15).
Data as JSON: /api/errors/9fb9d79974babdba.
Report an issue: GitHub.
Appendix: source
Thrown at app/graphql/mutations/delete_discussion_topic.rb:30
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
# 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::DeleteDiscussionTopic < Mutations::BaseMutation
graphql_name "DeleteDiscussionTopic"
# input arguments
argument :id, ID, required: true, prepare: GraphQLHelpers.relay_or_legacy_id_prepare_func("DiscussionTopic")
# the return data if the delete is successful
field :discussion_topic_id, ID, null: false
def resolve(input:)
record = DiscussionTopic.active.find_by(id: input[:id])
raise GraphQL::ExecutionError, "Unable to find Discussion Topic" if record.nil? || !record.grants_right?(current_user, nil, :read)
unless record.grants_right?(current_user, nil, :delete)
raise GraphQL::ExecutionError, "Insufficient permissions"
end
context[:deleted_models] = { discussion_topic: {} }
context[:deleted_models][:discussion_topic] = record
record.destroy
{
discussion_topic_id: record.id
}
end
def self.discussion_topic_id_log_entry(_topic, context)
context[:deleted_models][:discussion_topic]
end
end
View on GitHub (pinned to 1c9f0bb801)