{"record":{"id":"d9d4e670e1628ed7","repo":"instructure/canvas-lms","slug":"cannot-request-more-than-100-courses-at-once","errorCode":null,"errorMessage":"Cannot request more than 100 courses at once","messagePattern":"Cannot request more than 100 courses at once","errorType":"exception","errorClass":"GraphQL::ExecutionError","httpStatus":null,"severity":"error","filePath":"app/graphql/types/query_type.rb","lineNumber":260,"sourceCode":"                                    Enrollment.state_by_date_rank_sql,\n                                    Arel.sql(\"enrollments.id\")\n                                  )\n\n      InstructorQuery.new(deduplicated_ids_subquery)\n    end\n\n    field :courses,\n          [Types::CourseType],\n          \"Courses by IDs that are viewable by the current user\",\n          null: true do\n      argument :ids, [ID], \"graphql or legacy course IDs\", required: false, prepare: GraphQLHelpers.relay_or_legacy_ids_prepare_func(\"Course\")\n      argument :sis_ids, [String], \"ids from the original SIS system\", required: false\n    end\n    def courses(ids: nil, sis_ids: nil)\n      raise GraphQL::ExecutionError, \"Must specify exactly one of ids or sisIds\" if (ids && sis_ids) || !(ids || sis_ids)\n\n      course_ids = ids || sis_ids\n      raise GraphQL::ExecutionError, \"Cannot request more than 100 courses at once\" if course_ids&.length.to_i > 100\n\n      courses = if ids\n                  current_user&.accessible_courses_by_ids(ids, preload_courses: true)\n                elsif sis_ids\n                  current_user&.accessible_courses_by_sis_ids(sis_ids, preload_courses: true)\n                end\n\n      courses&.index_by(&:id)\n             &.values\n             &.sort_by! do |course|\n               Canvas::ICU.collation_key(course.nickname_for(current_user))\n             end\n    end\n\n    field :module_item, Types::ModuleItemType, null: true do\n      description \"ModuleItem\"\n      argument :id,\n               ID,","sourceCodeStart":242,"sourceCodeEnd":278,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/graphql/types/query_type.rb#L242-L278","documentation":"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.","triggerScenarios":"query { courses(ids: [/* 101+ ids */]) } or courses(sisIds: [/* 101+ */]) — any request whose list length exceeds 100 after passing the ids/sisIds exclusivity check.","commonSituations":"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.","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."],"exampleFix":"// before\nquery { courses(ids: $allIds) { _id name } } // allIds.length = 500\n// after\nconst batches = chunk(allIds, 100);\nconst results = await Promise.all(batches.map(ids => gql(`query($ids:[ID!]){ courses(ids:$ids){ _id name } }`, { ids })));","handlingStrategy":"validation","validationCode":"const list = vars.ids || vars.sisIds || [];\nif (list.length > 100) throw new Error(`courses accepts at most 100 ids, got ${list.length}`);","typeGuard":"const withinCourseLimit = (vars) => ((vars.ids || vars.sisIds) || []).length <= 100;","tryCatchPattern":"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; }","preventionTips":["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."],"tags":["graphql","validation","pagination"],"backgroundTag":"value-out-of-range","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"}