instructure/canvas-lms · error · GraphQL::ExecutionError
Cannot request more than 100 courses at once
Error message
Cannot request more than 100 courses at once
What it means
The `courses` resolver caps a single request at 100 course IDs (graphql/legacy or SIS). After the either/or argument check, it raises this GraphQL::ExecutionError when `ids` or `sis_ids` contains more than 100 entries.
Solutions
- Chunk the ID list into batches of at most 100 and issue multiple `courses` queries.
- Prefer the coursesConnection for enumeration instead of long ID lists.
- Filter the ID list server-side or by enrollment before querying so only needed courses are requested.
Example fix
// before
query { courses(ids: $allIds) { _id name } } // allIds.length = 500
// after
const batches = chunk(allIds, 100);
const results = await Promise.all(batches.map(ids => gql(`query($ids:[ID!]){ courses(ids:$ids){ _id name } }`, { ids }))); Defensive patterns
Strategy: validation
Validate before calling
const list = vars.ids || vars.sisIds || [];
if (list.length > 100) throw new Error(`courses accepts at most 100 ids, got ${list.length}`); Type guard
const withinCourseLimit = (vars) => ((vars.ids || vars.sisIds) || []).length <= 100;
Try / catch
try { return await gql(COURSES_QUERY, vars); } catch (e) { if (e.message.includes('more than 100 courses')) { return (await Promise.all(chunk(list, 100).map(b => gql(COURSES_QUERY, mkVars(b))))).flat(); } throw e; } Prevention
- Always chunk ID lists to 100 before calling courses.
- Prefer coursesConnection with pagination for unbounded enumeration.
- Add a client-side assertion on list length in your data layer so it never reaches the API.
When it happens
Trigger: query { courses(ids: [/* 101+ ids */]) } or courses(sisIds: [/* 101+ */]) — any request whose list length exceeds 100 after passing the ids/sisIds exclusivity check.
Common situations: Bulk dashboards exporting all enrollments in one query; admin tooling iterating over an account's courses without pagination; a sync job accumulating IDs and sending them in one shot.
Understand the failure class
Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.
Related errors
- A maximum of 50 assessees can be provided at once
- A maximum of 50 assessors can be provided at once
- All ConversationMessages must exist within the same…
- Anonymous assignments must be manually posted
- Assignment overrides are not allowed in the parent…
AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15).
Data as JSON: /api/errors/d9d4e670e1628ed7.
Report an issue: GitHub.
Appendix: source
Thrown at app/graphql/types/query_type.rb:260
Enrollment.state_by_date_rank_sql,
Arel.sql("enrollments.id")
)
InstructorQuery.new(deduplicated_ids_subquery)
end
field :courses,
[Types::CourseType],
"Courses by IDs that are viewable by the current user",
null: true do
argument :ids, [ID], "graphql or legacy course IDs", required: false, prepare: GraphQLHelpers.relay_or_legacy_ids_prepare_func("Course")
argument :sis_ids, [String], "ids from the original SIS system", required: false
end
def courses(ids: nil, sis_ids: nil)
raise GraphQL::ExecutionError, "Must specify exactly one of ids or sisIds" if (ids && sis_ids) || !(ids || sis_ids)
course_ids = ids || sis_ids
raise GraphQL::ExecutionError, "Cannot request more than 100 courses at once" if course_ids&.length.to_i > 100
courses = if ids
current_user&.accessible_courses_by_ids(ids, preload_courses: true)
elsif sis_ids
current_user&.accessible_courses_by_sis_ids(sis_ids, preload_courses: true)
end
courses&.index_by(&:id)
&.values
&.sort_by! do |course|
Canvas::ICU.collation_key(course.nickname_for(current_user))
end
end
field :module_item, Types::ModuleItemType, null: true do
description "ModuleItem"
argument :id,
ID,View on GitHub (pinned to 1c9f0bb801)