instructure/canvas-lms · warning
Child content tag was not found for #
Error message
Child content tag was not found for #{self.class.name} #{id} - either this is from old code or something bad happened What it means
MasterCourses::Restrictor#mark_downstream_changes records which columns changed on a locked master-course item by updating its child content tag. When no MasterCourses::ChildContentTag exists for the object, it logs this warning and skips tracking — either the item predates master-course locking code or a tag that should exist is missing.
Solutions
- Usually safe to ignore for legacy content: the change simply isn't tracked as a downstream change
- If sync tracking matters, re-run the master course association/tag migration to backfill missing child tags
- Verify the item belongs to a master template (check MasterCourses::MasterTemplate associations)
- Check for code deleting child tags and restore referential integrity
- master_courses_child_content_tags table
Example fix
// before
# silent warn on missing tag
Rails.logger.warn("Child content tag was not found ...")
// after
Rails.logger.warn("Child content tag was not found ...")
Canvas::Errors.capture(e_missing_tag, content_type: self.class.name, content_id: id) if template_content? Defensive patterns
Strategy: fallback
Validate before calling
tag = MasterCourses::ChildContentTag.where(
content_type: obj.class.name, content_id: obj.id
).first
puts('tag missing — change will not be synced') unless tag Type guard
def blueprint_managed?(obj) MasterCourses::ChildContentTag.exists?(content: obj) end
Try / catch
begin
mark_downstream_changes(columns)
rescue => e
Rails.logger.warn("downstream tracking failed: #{e.message}")
end Prevention
- Re-run blueprint tag backfill migrations for retrofitted master courses
- Only expect downstream tracking for items governed by a master template
- Monitor for code paths that delete child_content_tags
- Treat the warning as benign for legacy/old-code content
When it happens
Trigger: An edit to a master-course (blueprint) locked object whose child content tag lookup returns nil — objects created before the blueprint feature existed, tags deleted, or unsaved/non-master-template content passing through the restrictor.
Common situations: Old courses retrofitted as blueprint courses (items have no child tags); direct console/data fixes that removed tags; edits to content not actually governed by a master template; race conditions during template migration.
Understand the failure class
Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.
Related errors
- cannot change column: captions - locked by Master Course
- cannot start new migration while another one is running
- Document index status sync failed for ai_experience #
- invalid edit type
- invalid restriction type
AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15).
Data as JSON: /api/errors/1d81346d3421dc8d.
Report an issue: GitHub.
Appendix: source
Thrown at app/models/master_courses/restrictor.rb:175
changed_columns.delete(state_column)
changed_columns << "manually_deleted"
end
if changed_columns.any?
tag_content = if is_a?(Assignment) && (submittable = submittable_object)
submittable # mark on the owner's tag
else
self
end
MasterCourses::ChildContentTag.transaction do
child_tag = MasterCourses::ChildContentTag.where(content: tag_content).lock.first
if child_tag
new_changes = changed_columns - child_tag.downstream_changes
if new_changes.any?
child_tag.downstream_changes += new_changes
child_tag.save!
end
else
Rails.logger.warn("Child content tag was not found for #{self.class.name} #{id} - either this is from old code or something bad happened")
end
end
end
end
def create_child_content_tag
# i thought about making this a bulk insert at the end of the migration but the race conditions seemed scary
if @importing_migration && is_child_content?
@importing_migration.master_course_subscription.content_tag_for(self)
end
end
def check_before_overwriting_child_content_on_import
return unless @importing_migration && is_child_content?
child_tag = @importing_migration.master_course_subscription.content_tag_for(self) # find or create it
return unless child_tag && child_tag.downstream_changes.present?
View on GitHub (pinned to 1c9f0bb801)