instructure/canvas-lms · error · GraphQL::ExecutionError

invalid course: #

Error message

invalid course: #{course_id}

What it means

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.

Solutions

  1. Confirm the course id exists and is active: Course.find_by(id: course_id) in console
  2. Ensure you pass a legacy numeric id (use GraphQLHelpers.parse_relay_or_legacy_id-compatible input)
  3. Verify the current user has :create permission on assignments in that course (teacher/admin role)
  4. Check shard/account context matches the course

Example fix

// before
createAssignment(input: { courseId: "产量Q291cnNlLQ==" }) // raw relay id misparsed
// after
createAssignment(input: { courseId: "123" }, name: "HW", pointsPossible: 10) // as teacher
Defensive patterns

Strategy: validation

Validate before calling

const course = await canvas.get(`/api/v1/courses/${courseId}`)
const canCreate = course.enrollments?.some(e => ['teacher','ta','designer'].includes(e.enrollment_state === 'active' ? e.type.toLowerCase() : ''))
if (!course || course.state === 'deleted' || course.state === 'completed' || !canCreate) throw new Error(`invalid course: ${courseId}`)

Type guard

function isCreatableCourse(c) { return c != null && c.id != null && !['deleted','completed'].includes(c.workflow_state) && Array.isArray(c.enrollments) }

Try / catch

try {
  await client.mutate({ mutation: CREATE_ASSIGNMENT, variables: { input: { courseId } } })
} catch (e) {
  if (e.message.startsWith('invalid course:')) {
    // check id format and user role before retry
  }
  throw e
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15). Data as JSON: /api/errors/d9ee1ae3382916de. Report an issue: GitHub.

Appendix: source

Thrown at app/graphql/mutations/create_assignment.rb:35

# 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::CreateAssignment < Mutations::AssignmentBase::Mutation
  graphql_name "CreateAssignment"

  argument :course_id, ID, required: true
  argument :name, String, required: true
  argument :secure_params, String, required: false
  # most arguments inherited from AssignmentBase

  def resolve(input:, submittable: nil)
    course_id = GraphQLHelpers.parse_relay_or_legacy_id(input[:course_id], "Course")

    @course = Course.find_by(id: course_id)
    @working_assignment = @course.assignments.build if @course

    raise GraphQL::ExecutionError, "invalid course: #{course_id}" unless @working_assignment&.grants_right? current_user, :create

    # initialize published argument
    @working_assignment.workflow_state = "unpublished"
    input_hash = input.to_h
    if input_hash.key? :state
      asked_state = input_hash.delete :state
      case asked_state
      when "unpublished"
        input_hash[:published] = false
      when "published"
        input_hash[:published] = true
      else
        raise "unable to handle state change: #{asked_state}"
      end
    end

    if submittable
      submittable.assignment = @working_assignment

View on GitHub (pinned to 1c9f0bb801)