{"record":{"id":"0cfe6807247f5e45","repo":"instructure/canvas-lms","slug":"not-found-update-discussion-topic-participant","errorCode":null,"errorMessage":"not found","messagePattern":"not found","errorType":"exception","errorClass":"GraphQL::ExecutionError","httpStatus":null,"severity":"error","filePath":"app/graphql/mutations/update_discussion_topic_participant.rb","lineNumber":40,"sourceCode":"  graphql_name \"UpdateDiscussionTopicParticipant\"\n\n  argument :discussion_topic_id, ID, required: true, prepare: GraphQLHelpers.relay_or_legacy_id_prepare_func(\"DiscussionEntry\")\n  argument :expanded, Boolean, required: false\n  argument :has_unread_pinned_entry, Boolean, required: false\n  argument :preferred_language, Types::PreferredLanguageType, required: false\n  argument :show_pinned_entries, Boolean, required: false\n  argument :sort_order, Types::DiscussionSortOrderType, required: false\n  argument :summary_enabled, Boolean, required: false\n  field :discussion_topic, Types::DiscussionType, null: false\n\n  def resolve(input:)\n    discussion_topic = DiscussionTopic.find(input[:discussion_topic_id])\n    raise GraphQL::ExecutionError, \"insufficient permission\" unless discussion_topic.grants_right?(current_user, session, :read)\n\n    discussion_topic.update_or_create_participant(current_user:, **input)\n    { discussion_topic: }\n  rescue ActiveRecord::RecordNotFound\n    raise GraphQL::ExecutionError, \"not found\"\n  end\nend\n","sourceCodeStart":22,"sourceCodeEnd":43,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/graphql/mutations/update_discussion_topic_participant.rb#L22-L43","documentation":"This is a deliberately translated error inside the updateDiscussionTopicParticipant GraphQL mutation. The resolver loads the DiscussionTopic by id, and any ActiveRecord::RecordNotFound (or an id belonging to a non-discussion object, since DiscussionTopic.find uses the ID type) is rescued and re-raised as a GraphQL::ExecutionError with the message \"not found\". GraphQL clients see it in the errors array rather than as an unhandled exception.","triggerScenarios":"Calling mutation updateDiscussionTopicParticipant with a discussion_topic_id that does not exist, is soft-deleted, belongs to a different shard, or is a non-topic object whose ID collides in the Relay ID decoding.","commonSituations":"Clients caching a topic id after the topic was deleted; passing a legacy numeric id instead of the expected base64/Relay id (or vice versa depending on the prepare function); multi-shard setups where the id is not shard-prefixed so Switchman looks it up on the wrong shard.","solutions":["Verify the discussion_topic_id exists and is not deleted: DiscussionTopic.unscoped.find_by(id: ...).","Ensure the client sends the correctly encoded Relay/legacy id expected by the mutation's ID prepare function.","In multi-shard environments, use the shard-prefixed id so the record is found on the correct shard.","Check the acting user has :read permission on the topic, since the same flow can 404/fail when access is lost.","Handle the errors array in the GraphQL client and prompt the user to refresh/reselect the discussion."],"exampleFix":"// before\nupdateDiscussionTopicParticipant(input: { discussionTopicId: \"17\", ... })\n// after\n// resolve the id through a fresh query first\ntopic = DiscussionTopic.unscoped.find_by(id: id)\nraise \"topic missing\" unless topic\nupdateDiscussionTopicParticipant(input: { discussionTopicId: topic.relay_id, ... })","handlingStrategy":"try-catch","validationCode":"// before calling the mutation\nconst topic = await fetchDiscussionTopic(id)\nif (!topic) throw new Error(`discussion topic ${id} does not exist`)","typeGuard":"const isTopic = (t) => !!t && typeof t.id === 'string' && t.__typename === 'DiscussionTopic'","tryCatchPattern":"try {\n  await updateDiscussionTopicParticipant(input)\n} catch (e) {\n  if (e.graphQLErrors?.some(g => g.message === 'not found')) {\n    refreshDiscussionList() // topic likely deleted or id invalid\n  } else { throw e }\n}","preventionTips":["Always source the topic id from a fresh query, not long-lived client cache.","Use shard-prefixed ids in multi-shard Canvas deployments.","Check the user can :read the topic before offering the mutation in UI."],"tags":["graphql","ruby","activerecord","canvas-lms"],"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"}