instructure/canvas-lms · error · ArgumentError
Needs block or destination path
Error message
Needs block or destination path
What it means
CanvasUnzip.extract_archive requires either a destination folder path or a block; it raises ArgumentError "Needs block or destination path" when called with neither (dest_folder nil and no block_given?). The API supports two modes — write to disk or stream entries to a block — and this call matches neither.
Solutions
- Pass a destination directory string: CanvasUnzip.extract_archive(path, '/tmp/extracted').
- Or supply a block: CanvasUnzip.extract_archive(path) { |entry, i| ... }.
- Fix the code path that left dest_folder nil (check ENV/setting lookup defaults).
- If both modes are conditional, add an explicit error/validation in your own code before calling.
Example fix
// before
CanvasUnzip.extract_archive(zip_path)
// after
CanvasUnzip.extract_archive(zip_path, Rails.root.join('tmp/uploads/extracted').to_s) Defensive patterns
Strategy: validation
Validate before calling
raise ArgumentError, "need dest_folder or block" if dest_folder.blank? && !block_given?
Type guard
def extraction_args_valid?(dest_folder) !dest_folder.nil? && !dest_folder.to_s.empty? end
Try / catch
begin CanvasUnzip.extract_archive(path, dest) rescue ArgumentError => e raise e unless e.message == "Needs block or destination path" raise UsageError, "extract_archive requires dest_folder or a block" end
Prevention
- Pick one extraction mode (path or block) per call site
- Validate settings that supply dest paths with defaults
- Avoid passing possibly-nil dest variables
- Add call-site guards when wrapping the API
When it happens
Trigger: CanvasUnzip.extract_archive('x.zip') with no second argument and no block; calling with dest_folder: nil explicitly (e.g. variable that failed to initialize) in a keyword-args-capable call site.
Common situations: Refactoring from block style to path style and removing both; a dest variable derived from settings that is blank; copy-paste of each_entry-style calls into extract_archive.
Understand the failure class
Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.
Related errors
- no block given
- File not found
- block is required if module_name is not passed
- Cannot generate a services JWT without a 'sub' entry
- Cannot include a concatenation in a merge.
AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15).
Data as JSON: /api/errors/88dc890a2042cb3b.
Report an issue: GitHub.
Appendix: source
Thrown at gems/canvas_unzip/lib/canvas_unzip.rb:82
# if a destination path is given, the archive will be extracted to that location
# * files will be skipped if they already exist
# if no destination path is given, a block must be given,
# * yields |entry, index| for each (safe) zip/tar entry available to be extracted
# returns a hash of lists of entries that were skipped by reason
# { :unsafe => [list of entries],
# :already_exists => [list of entries],
# :filename_too_long => [list of entries],
# :unknown_compression_method => [list of entries] }
def self.extract_archive(archive_filename, dest_folder = nil, limits: nil, nested_dir: nil)
warnings = {}
limits ||= default_limits(File.size(archive_filename))
bytes_left = limits.maximum_bytes
files_left = limits.maximum_files
raise ArgumentError, "File not found" unless File.exist?(archive_filename)
raise ArgumentError, "Needs block or destination path" unless dest_folder || block_given?
each_entry(archive_filename) do |entry, index|
if unsafe_entry?(entry)
add_warning(warnings, entry, :unsafe)
next
end
if block_given?
yield(entry, index)
else
raise FileLimitExceeded if files_left <= 0
begin
name = entry.name
name = name.sub(nested_dir, "") if nested_dir # pretend the dir doesn't exist
f_path = File.join(dest_folder, name)
entry.extract(f_path, maximum_size: bytes_left) do |size|
bytes_left -= sizeView on GitHub (pinned to 1c9f0bb801)