Locked Canvas assignments leave your to-do list entirely

When a Canvas assignment locks — say, a quiz that closes at its due date — it doesn't just become unsubmittable; it leaves your to-do list entirely. The feed filters locked items out by default (not_locked in the to-do scope), so a closed quiz you missed leaves nothing on the list to tell you that you missed it. The same filter also hides an assignment that hasn't opened yet.

What you see

A quiz with an "Available until" date passes its cutoff overnight. The next morning your to-do list is shorter and looks clean. Nothing on the list says an item was removed, and nothing distinguishes "you finished this" from "this closed while you weren't looking." The first sign that something went unfinished is usually the grade.

The mirror image happens at the other end of the window: an exam with an "Available from" date next week does not appear on the list now, even if its due date falls inside the feed's window.

Why it happens

The scope in Canvas's source that builds the to-do feed applies a not_locked filter unless the caller explicitly opts into locked items — and the to-do endpoint does not opt in. Canvas counts an assignment as locked both before its "Available from" date and after its "Available until" date, so the same filter hides work that hasn't opened yet and work that has closed. The behavior is the same whether the item was submitted, partially done, or never opened, because the filter is about lock state, not about you.

This is one of several distinct routes by which work leaves the Canvas to-do feed while still being real and unfinished — a short list that also includes the feed's one-week forward window and offline assignments dropping off at the deadline.

What you can do about it

This is one reason Margin does not treat an item leaving a feed as evidence it was done — absence is not completion, and closing a task requires a positive per-user signal.

Evidence

canvas-lms, app/models/user_learning_object_scopes.rb:276 (master, retrieved 2026-07-30):

assignments = assignments.not_locked unless include_locked

include_locked defaults to false at :242, and users_controller.rb:975-980 — the todo_items action behind GET /api/v1/users/self/todo — calls assignments_needing_submitting without passing include_locked, so the to-do endpoint does not opt in.

canvas-lms, app/models/abstract_assignment.rb:3520 (master, retrieved 2026-07-30) defines the filter itself:

scope :not_locked, -> {
  where("(assignments.unlock_at IS NULL OR assignments.unlock_at<:now)
     AND (assignments.lock_at IS NULL OR assignments.lock_at>:now)", now: Time.zone.now)
}

The scope has two arms: an assignment is excluded while its unlock_at ("Available from") is still in the future, and again once its lock_at ("Available until") has passed. All four anchors — :276, :242, abstract_assignment.rb:3520 and users_controller.rb:975-980 — were verified against live master on 2026-07-30.

Back to the Canvas Quirks Encyclopedia