instructure/canvas-lms · critical · Bundler::GemNotFound

bundler-multilock plugin is not installed

Error message

bundler-multilock plugin is not installed

What it means

gems/gemfile_prefix.rb wires in Canvas's vendored bundler-multilock plugin to manage multiple Gemfile.lock variants. It declares the plugin dependency itself and then immediately verifies it is actually installed; if Bundler loads this prefix outside plugin evaluation (so `is_a?(Bundler::Plugin::DSL)` is false) and the plugin is not installed, it raises GemNotFound. This means the vendored gem directory is missing or the plugin was never installed during setup.

Solutions

  1. Re-run Canvas bootstrap / `bundle install` from the repo root so the vendored plugin installs
  2. Verify gems/vendor/gems/bundler-multilock exists and is intact (restore if deleted)
  3. Clear Bundler plugin cache and reinstall: `bundler plugin uninstall bundler-multilock` then bundle again
  4. Confirm you are using the Bundler version Canvas expects (see Gemfile / docs)

Example fix

// shell
# before: bundle exec rake  => GemNotFound: bundler-multilock plugin is not installed
# after:
rm -rf ~/.local/share/bundler-plugin  # or bundler plugin uninstall bundler-multilock
bundle install
bundle exec rake
Defensive patterns

Strategy: validation

Validate before calling

require 'bundler'
abort 'bundler-multilock missing; run bundle install' unless Bundler::Plugin.installed?('bundler-multilock')

Prevention

When it happens

Trigger: Running bundler (bundle install, bundle exec, script/anything loading the Gemfile) from the main Gemfile when vendor/gems/bundler-multilock has not been installed/registered, e.g. a fresh checkout with an incomplete setup or deleted vendor directory.

Common situations: Fresh clone without running the full bootstrap; CI image stripping vendor/gems; upgrading Bundler and plugin state being lost; running bundler from a sub-gem's gemfile with a broken parent resolution.

Understand the failure class

Background: "not installed", "pip install", "required for": how missing-dependency errors surface across open-source libraries — this error's family across 34 libraries.

Related errors


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

Appendix: source

Thrown at gems/gemfile_prefix.rb:26

# Canvas is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.

# don't define additional lockfiles unless we're being called as part of a vendored gem
# (i.e. we're not being called from the main Gemfile)
Pathname.include(Comparable)
return unless Bundler.default_gemfile.dirname > Pathname.new(__dir__)

plugin "bundler-multilock", "1.2.3", path: File.expand_path("../vendor/gems/bundler-multilock", __dir__)
raise GemNotFound, "bundler-multilock plugin is not installed" if !is_a?(Bundler::Plugin::DSL) && !Plugin.installed?("bundler-multilock")
return unless Plugin.installed?("bundler-multilock")

Plugin.send(:load_plugin, "bundler-multilock")

require_relative "../config/canvas_rails_switcher" unless defined?($canvas_rails)

canvas_default_lockfile = "../../Gemfile.lock"

current_rails = $canvas_rails
SUPPORTED_RAILS_VERSIONS.each do |rails_version|
  lockfile = "rails#{rails_version.delete(".")}"
  parent = if rails_version == SUPPORTED_RAILS_VERSIONS.first
             lockfile = nil
             canvas_default_lockfile
           else
             "../../Gemfile.#{lockfile}.lock"
           end

View on GitHub (pinned to 1c9f0bb801)