instructure/canvas-lms · error · GraphQL::ExecutionError
Not authorized to view this user's module progress
Error message
Not authorized to view this user's module progress
What it means
apply_module_filters determines the target user (from filter user_id or the current user) and checks can_view_user_module_progress? before applying Modules::FilterByCompletion. If the requesting user lacks permission to view that target user's progress, it raises 'Not authorized to view this user's module progress'.
Solutions
- Query only your own module progress (omit user_id) unless you are a permitted observer/teacher
- Verify the user_id global ID is correct and belongs to the same course
- Check the enrolling user as an observer if viewing a student's progress
- Confirm course-level permissions (teacher/admin) before filtering on another user
Example fix
// before
modules(filter: { completionStatus: "incomplete", userId: classmateGid })
// after
modules(filter: { completionStatus: "incomplete" }) // own progress only Defensive patterns
Strategy: try-catch
Validate before calling
const isSelf = targetUserId === currentUser.id const isPermittedObserver = currentUser.observerOf?.includes(targetUserId) const isTeacher = currentUser.enrollments?.some(e => e.type === 'TeacherEnrollment' && e.courseId === courseId) if (!(isSelf || isPermittedObserver || isTeacher)) omitUserIdFilter()
Type guard
function canViewProgress(me, target, courseId) { return me?.id === target || me?.observerOf?.includes(target) || me?.enrollments?.some(e => e.type === 'TeacherEnrollment' && e.courseId === courseId) } Try / catch
try { await query(MODULES_QUERY) } catch (e) { if (e.message.startsWith('Not authorized')) { fallBackToOwnProgress() } } Prevention
- Default to querying your own progress (omit user_id)
- Verify observer/teacher enrollment before filtering on others
- Validate the user_id global id belongs to the same course
When it happens
Trigger: Querying course.modules with filter.completion_status for a user_id the current user is not allowed to observe (not self, not an observer/student of that user, not a teacher in the course).
Common situations: Observer/parent tooling querying the wrong user id; students attempting to view classmates' progress; stale or global-id mismatch causing the permission lookup to fail.
Understand the failure class
Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.
Related errors
- Authentication required to view other users' module progress
- feature flag is disabled
- insufficient permission
- insufficient permission
- insufficient permission
AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15).
Data as JSON: /api/errors/287ffc79cfe8a076.
Report an issue: GitHub.
Appendix: source
Thrown at app/graphql/types/course_type.rb:741
# For unauthenticated users, only "incomplete" filter returns modules
# All other filters return empty since they have no progress
case filter[:completion_status]
when "incomplete"
return scope # All modules are incomplete for unauthenticated users
else
return scope.none # No completed/in_progress/not_started modules
end
end
target_user = if filter[:user_id]
User.find(filter[:user_id])
else
current_user
end
# Check permissions before applying filter
unless can_view_user_module_progress?(target_user)
raise GraphQL::ExecutionError, "Not authorized to view this user's module progress"
end
scope = Modules::FilterByCompletion.new(
scope,
filter[:completion_status],
target_user,
current_user,
course
).filter
end
scope
end
def can_view_user_module_progress?(user)
# Users can always view their own progress
return true if user.id == current_user.id
View on GitHub (pinned to 1c9f0bb801)