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
- 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
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
- 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
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
- Course not found
- not found
- A course with that id does not exist
- All ConversationMessages must exist within the same…
- An unexpected error occurred while grading.
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_assignmentView on GitHub (pinned to 1c9f0bb801)