instructure/canvas-lms · error · ActiveRecord::RecordNotFound

invalid assignment override id #

Error message

invalid assignment override id #{override_data["id"]} for assignment #{assignment.id}

What it means

In Assignment::BulkUpdate#run, after locating the assignment, each override entry must match an AssignmentOverride belonging to that assignment (matched by id in the loaded association). If no override with that id exists on the assignment, it raises ActiveRecord::RecordNotFound 'invalid assignment override id X for assignment Y'. This guards against updating overrides from a different assignment or deleted overrides.

Solutions

  1. Re-fetch assignment overrides immediately before the bulk update and filter payload entries to existing ids
  2. Confirm override ids belong to the same assignment as the enclosing entry (assignment_id check client-side)
  3. Handle deleted overrides gracefully by dropping them from the payload instead of raising
  4. Rescue ActiveRecord::RecordNotFound and surface which override/assignment pair was invalid

Example fix

// before
overrides.each { |o| run_entry(assignment, o) } # may reference stale ids
// after
valid = assignment.assignment_overrides.reload.map(&:id)
overrides.select { |o| valid.include?(o["id"].to_i) }.each { |o| run_entry(assignment, o) }
Defensive patterns

Strategy: try-catch

Validate before calling

const overrideIds = new Map(assignments.map(a => [a.id, a.assignment_overrides.map(o => o.id)]));
for (const [aid, entries] of payload)
  for (const e of entries)
    if (!overrideIds.get(aid)?.includes(e.id)) throw new Error(`override ${e.id} not on assignment ${aid}`);

Type guard

const overrideOnAssignment = (overrideId, assignment) =>
  assignment.assignment_overrides.some(o => o.id === overrideId)

Try / catch

begin
  Assignment::BulkUpdate.new(course, user, payload).run
rescue ActiveRecord::RecordNotFound => e
  render json: { error: e.message }, status: :not_found
end

Prevention

When it happens

Trigger: Payload references an override id belonging to a different assignment; the override was deleted (e.g. section removed) before the bulk update; override id belongs to an assignment the user can't see; id passed as string that doesn't .to_i-match any loaded override.

Common situations: Stale bulk-edit UI after another teacher deleted an override; copy/paste of override ids across assignments in API scripts; client bug sending all overrides under the wrong assignment key.

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


AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15). Data as JSON: /api/errors/f8eb490539ceac20. Report an issue: GitHub.

Appendix: source

Thrown at app/models/assignment/bulk_update.rb:57

      next unless dates.present?

      base, overrides = dates.partition { |date| date["base"] }

      # 1a. update the assignment
      assignment = assignments[id.to_i]
      raise ActiveRecord::RecordNotFound, "invalid assignment id #{id}" unless assignment

      if base.any?
        assignment.content_being_saved_by(@current_user)
        assignment.updating_user = @current_user
        assignment.assign_attributes(base.first.slice(*%w[due_at unlock_at lock_at]))
        assignments_to_save << assignment if assignment.changed?
      end

      # 1b. update associated overrides
      overrides.each do |override_data|
        override = assignment.assignment_overrides.detect { |o| o.id == override_data["id"].to_i }
        raise ActiveRecord::RecordNotFound, "invalid assignment override id #{override_data["id"]} for assignment #{assignment.id}" unless override

        %w[due_at unlock_at lock_at].each do |date|
          if override_data.key?(date)
            override.send(:"#{date}=", override_data[date])
            override.send(:"#{date}_overridden=", true)
          else
            override.send(:"#{date}=", nil)
            override.send(:"#{date}_overridden=", false)
          end
        end
        assignments_to_save << assignment if override.changed?
      end
    end

    progress_count = 0
    progress_total = assignments_to_save.size * 2

    # 2. validate all assignments and overrides

View on GitHub (pinned to 1c9f0bb801)