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

  1. Pass a destination directory string: CanvasUnzip.extract_archive(path, '/tmp/extracted').
  2. Or supply a block: CanvasUnzip.extract_archive(path) { |entry, i| ... }.
  3. Fix the code path that left dest_folder nil (check ENV/setting lookup defaults).
  4. 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

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


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 -= size

View on GitHub (pinned to 1c9f0bb801)