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
- Re-run Canvas bootstrap / `bundle install` from the repo root so the vendored plugin installs
- Verify gems/vendor/gems/bundler-multilock exists and is intact (restore if deleted)
- Clear Bundler plugin cache and reinstall: `bundler plugin uninstall bundler-multilock` then bundle again
- 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
- Run full bootstrap on fresh clones and CI
- Keep gems/vendor/gems/bundler-multilock intact
- Pin the expected Bundler version
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
- A new_id, '# ', referenced an existing # and the # with #…
- A new_integration_id, '#
- A student referenced a non-existent user #
- A user did not pass validation
- [A11Y Scan] Skipped resource #
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)