{"record":{"id":"968381ac518d79ce","repo":"instructure/canvas-lms","slug":"a-course-with-that-id-does-not-exist","errorCode":null,"errorMessage":"A course with that id does not exist","messagePattern":"A course with that id does not exist","errorType":"exception","errorClass":"GraphQL::ExecutionError","httpStatus":null,"severity":"error","filePath":"app/graphql/mutations/set_course_post_policy.rb","lineNumber":33,"sourceCode":"# 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#\n\nclass Mutations::SetCoursePostPolicy < Mutations::BaseMutation\n  graphql_name \"SetCoursePostPolicy\"\n\n  argument :course_id, ID, required: true, prepare: GraphQLHelpers.relay_or_legacy_id_prepare_func(\"Course\")\n  argument :post_manually, Boolean, required: true\n\n  field :post_policy, Types::PostPolicyType, null: true\n\n  def resolve(input:)\n    begin\n      course = Course.find(input[:course_id])\n    rescue ActiveRecord::RecordNotFound\n      raise GraphQL::ExecutionError, \"A course with that id does not exist\"\n    end\n\n    # checking if the current user has manage_course_content_edit permission in the current course\n    perms = %i[manage_course_content_edit manage_grades]\n    verify_any_authorized_actions!(course, perms)\n\n    course.apply_post_policy!(post_manually: input[:post_manually])\n    { post_policy: course.default_post_policy }\n  end\n\n  def self.post_policy_log_entry(post_policy, _context)\n    post_policy.course\n  end\nend\n","sourceCodeStart":15,"sourceCodeEnd":48,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/graphql/mutations/set_course_post_policy.rb#L15-L48","documentation":"Raised by the SetCoursePostPolicy GraphQL mutation when Course.find(input[:course_id]) raises ActiveRecord::RecordNotFound, i.e. no Course exists with the supplied id. The mutation rescues the Rails exception and re-raises it as a GraphQL::ExecutionError with a stable client-facing message so callers get a clean GraphQL error instead of an unhandled record-not-found.","triggerScenarios":"Executing the setCoursePostPolicy mutation with a course_id that (a) never existed, (b) was deleted (soft/hard delete removing it from the default scope), (c) is a non-integer/garbage id causing find to fail, or (d) lives on a different shard than the one the request is scoped to.","commonSituations":"Stale course ids cached in a frontend after a course deletion; copy-pasted IDs from another Canvas environment (test vs production); cross-shard id handling mistakes in Canvas's Switchman-sharded setup; typos or id/name confusion when scripting API calls.","solutions":["Verify the course_id exists (e.g. GET /api/v1/courses/:id) and correct the id passed to the mutation","Confirm the request is routed to the correct shard/root account for that course id","If the course was deleted, restore it or target a different course; deleted courses cannot receive post policies","Use the course's actual numeric id rather than a slug, sis id, or lti id — convert sis ids via the sis_course_id API lookups if needed"],"exampleFix":"// before: unverified id\nsetCoursePostPolicy(input: { courseId: params.courseRef, ... })\n\n// after: resolve and validate the course first\nconst course = await canvas.get(`/api/v1/courses/${params.courseRef}`);\nif (!course?.id) throw new Error(`course not found: ${params.courseRef}`);\nsetCoursePostPolicy(input: { courseId: course.id, ... });","handlingStrategy":"validation","validationCode":"async function requireCourse(courseId) {\n  const res = await fetch(`/api/v1/courses/${courseId}`);\n  if (!res.ok) throw new Error(`Course ${courseId} does not exist (HTTP ${res.status})`);\n  return res.json();\n}","typeGuard":"const isCourseRef = (v) =>\n  (typeof v === 'number' && Number.isInteger(v) && v > 0) ||\n  (typeof v === 'string' && /^\\d+$/.test(v));","tryCatchPattern":"try {\n  await setCoursePostPolicy({ variables: { courseId, ... } });\n} catch (e) {\n  if (e.message === 'A course with that id does not exist') {\n    redirectToCoursePicker();\n  } else { throw e; }\n}","preventionTips":["Always pass the numeric Canvas course id, never sis ids, slugs, or names","Fetch the course id from the API/canvas environment rather than hardcoding across environments","Handle deleted courses: re-check course existence after any deletion workflow","In sharded setups, keep GraphQL calls on the same root account/shard as the course"],"tags":["graphql","canvas-lms","record-not-found","course"],"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"}