instructure/canvas-lms · critical

you must run "webpack" first

Error message

you must run "webpack" first

What it means

The webpack CDN registry reads public/<asset_dir>/webpack-manifest.json to translate logical js bundle names to fingerprinted output files. In production a missing manifest raises 'you must run "webpack" first'; dev returns {} so lookups degrade gracefully.

Solutions

  1. Run the webpack production build (yarn webpack) to emit webpack-manifest.json into the expected asset dir
  2. Verify CONFIG/@asset_dir matches the directory the build writes to
  3. Add the webpack build as a required step in the production image build
  4. Check the manifest exists in a startup health check

Example fix

// before
file = Rails.root.join("public/#{@asset_dir}/webpack-manifest.json") # missing -> raise in prod
// after
$ yarn webpack   # regenerates public/dist/webpack-manifest.json
Defensive patterns

Strategy: fallback

Validate before calling

raise 'webpack manifest missing' unless Rails.root.join('public/dist/webpack-manifest.json').exist?

Try / catch

begin
  registry.load_manifest_from_disk
rescue RuntimeError => e
  raise unless e.message.include?('webpack')
  {}
end

Prevention

When it happens

Trigger: Resolving any webpack-managed asset in production when public/<asset_dir>/webpack-manifest.json is absent because the webpack build never ran or its output was deleted.

Common situations: Deploying a production image without the webpack build stage; a failed/partial webpack run that left no manifest; cleaning public/ before serving; misconfigured @asset_dir.

Related errors


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

Appendix: source

Thrown at lib/canvas/cdn/registry/webpack.rb:67

          # contains -entry- in filename, ends in .js, but doesn't end in .map.js
          all_entries = @manifest.values.grep(/-entry-.*\.js$/).grep_v(/\.map\.js$/).map { |x| realpath(x) }

          # partition entries into main and others
          main_entry, other_entries = all_entries.partition { |x| x.include? "main-entry" }

          # load other entries first, then main entry
          other_entries + main_entry
        end

        private

        def load_manifest_from_disk
          file = Rails.root.join("public/#{@asset_dir}/webpack-manifest.json")

          if file.exist?
            JSON.parse(file.read).freeze
          elsif Rails.env.production?
            raise "you must run \"webpack\" first"
          else
            {}
          end
        end

        def realpath(vfile)
          "/#{@asset_dir}/#{vfile}"
        end
      end
    end
  end
end

View on GitHub (pinned to 1c9f0bb801)