{"record":{"id":"d9ee1ae3382916de","repo":"instructure/canvas-lms","slug":"invalid-course-course-id","errorCode":null,"errorMessage":"invalid course: #{course_id}","messagePattern":"invalid course: #(.+?)","errorType":"exception","errorClass":"GraphQL::ExecutionError","httpStatus":null,"severity":"error","filePath":"app/graphql/mutations/create_assignment.rb","lineNumber":35,"sourceCode":"# 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::CreateAssignment < Mutations::AssignmentBase::Mutation\n  graphql_name \"CreateAssignment\"\n\n  argument :course_id, ID, required: true\n  argument :name, String, required: true\n  argument :secure_params, String, required: false\n  # most arguments inherited from AssignmentBase\n\n  def resolve(input:, submittable: nil)\n    course_id = GraphQLHelpers.parse_relay_or_legacy_id(input[:course_id], \"Course\")\n\n    @course = Course.find_by(id: course_id)\n    @working_assignment = @course.assignments.build if @course\n\n    raise GraphQL::ExecutionError, \"invalid course: #{course_id}\" unless @working_assignment&.grants_right? current_user, :create\n\n    # initialize published argument\n    @working_assignment.workflow_state = \"unpublished\"\n    input_hash = input.to_h\n    if input_hash.key? :state\n      asked_state = input_hash.delete :state\n      case asked_state\n      when \"unpublished\"\n        input_hash[:published] = false\n      when \"published\"\n        input_hash[:published] = true\n      else\n        raise \"unable to handle state change: #{asked_state}\"\n      end\n    end\n\n    if submittable\n      submittable.assignment = @working_assignment","sourceCodeStart":17,"sourceCodeEnd":53,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/graphql/mutations/create_assignment.rb#L17-L53","documentation":"create_assignment#resolve raises 'invalid course: <id>' when the course lookup fails or the resulting (unsaved) assignment does not grant :create to the current user. Course.find_by(id:) returns nil for missing courses, and the grants_right? check also fails for unauthorized users, so both not-found and permission failures surface with this same message including the parsed course id.","triggerScenarios":"Calling createAssignment with a courseId that doesn't exist, is deleted/concluded, is a cross-shard/relay-id mismatch, or when the user lacks :create permission on the course's assignments.","commonSituations":"Passing a canvas global id or relay id where a legacy numeric id is expected (or vice versa) so find_by(id:) misses; student token trying to create assignments; course soft-deleted or concluded.","solutions":["Confirm the course id exists and is active: Course.find_by(id: course_id) in console","Ensure you pass a legacy numeric id (use GraphQLHelpers.parse_relay_or_legacy_id-compatible input)","Verify the current user has :create permission on assignments in that course (teacher/admin role)","Check shard/account context matches the course"],"exampleFix":"// before\ncreateAssignment(input: { courseId: \"产量Q291cnNlLQ==\" }) // raw relay id misparsed\n// after\ncreateAssignment(input: { courseId: \"123\" }, name: \"HW\", pointsPossible: 10) // as teacher","handlingStrategy":"validation","validationCode":"const course = await canvas.get(`/api/v1/courses/${courseId}`)\nconst canCreate = course.enrollments?.some(e => ['teacher','ta','designer'].includes(e.enrollment_state === 'active' ? e.type.toLowerCase() : ''))\nif (!course || course.state === 'deleted' || course.state === 'completed' || !canCreate) throw new Error(`invalid course: ${courseId}`)","typeGuard":"function isCreatableCourse(c) { return c != null && c.id != null && !['deleted','completed'].includes(c.workflow_state) && Array.isArray(c.enrollments) }","tryCatchPattern":"try {\n  await client.mutate({ mutation: CREATE_ASSIGNMENT, variables: { input: { courseId } } })\n} catch (e) {\n  if (e.message.startsWith('invalid course:')) {\n    // check id format and user role before retry\n  }\n  throw e\n}","preventionTips":["Pass legacy numeric course ids where the schema expects them","Verify teacher/TA/designer role with :create permission on the course","Check the course is not deleted or concluded","Ensure shard context matches the course account"],"tags":["graphql","mutation","course","authorization"],"backgroundTag":"invalid-identifier","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"}