instructure/canvas-lms · error
invalid association
Error message
invalid association
What it means
Course#clear_todo_list_cache_later validates that the association type it schedules a delayed job for is actually a course association whose klass is User. If the symbol doesn't name a known association (or names one not backed by User), it raises 'invalid association' before enqueueing the job.
Solutions
- Pass one of the existing User associations on Course (e.g. :admins, :teachers, :students, :tas).
- Define the missing has_many association on Course with class_name 'User' before calling.
- Fix the symbol typo at the call site; check with course.respond_to?(association_type) and Course.association(association_type).klass first.
Example fix
// before course.clear_todo_list_cache_later(:instructor) // after course.clear_todo_list_cache_later(:teachers)
Defensive patterns
Strategy: validation
Validate before calling
ok = Course.new.association(sym).klass == User rescue false
Type guard
def user_association?(course, sym) course.association(sym.to_sym).klass == User rescue ArgumentError false end
Prevention
- Restrict calls to a constant allowlist of association symbols
- Add unit tests for every clear_todo_list_cache_later call site
- Check association existence with respond_to?/association before calling
When it happens
Trigger: Calling course.clear_todo_list_cache_later(:some_symbol) where :some_symbol is not an association defined on Course, or is an association that isn't to User (e.g. an association to another model or nonexistent).
Common situations: Plugin/patch code adds a new admin-like association but passes the wrong symbol; renaming a Course association (e.g. :admins, :teachers, :students, :ta) without updating clear_todo_list_cache_later call sites; typo'd symbol in custom invalidation code.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- Course pace is deleted
- Course pace is not deleted
- Course required
- invalid options - # (must be in # )
- publishing disallowed for this publishing user
AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15).
Data as JSON: /api/errors/214c05c9d351e0c7.
Report an issue: GitHub.
Appendix: source
Thrown at app/models/course.rb:4591
# RUBY 2.7 this can go away (**{} will work at the caller)
raise ArgumentError, "Only send one hash" if !changes.empty? && !kwargs.empty?
changes = kwargs if changes.empty? && !kwargs.empty?
if changes[:is_public] || changes[:is_public_to_auth_users]
assignments.touch_all
attachments.touch_all
calendar_events.touch_all
context_modules.touch_all
discussion_topics.touch_all
quizzes.touch_all
wiki.touch
wiki_pages.touch_all
end
end
def clear_todo_list_cache_later(association_type)
raise "invalid association" unless association(association_type).klass == User
delay(run_at: 15.seconds.from_now, singleton: "course_clear_cache_#{global_id}_#{association_type}", on_conflict: :loose)
.clear_todo_list_cache(association_type)
end
def clear_todo_list_cache(association_type)
raise "invalid association" unless association(association_type).klass == User
send(association_type).clear_cache_keys(:todo_list)
end
def touch_admins # TODO: remove after existing jobs run
clear_todo_list_cache(:admins)
end
def clear_caches_if_necessary
clear_cache_key(:account_associations) if saved_change_to_root_account_id? || saved_change_to_account_id?
endView on GitHub (pinned to 1c9f0bb801)