instructure/canvas-lms · error · ArgumentError

no block given

Error message

no block given

What it means

CanvasUnzip.each_entry iterates archive entries via a caller-supplied block and raises ArgumentError "no block given" when invoked without one. It's an internal/low-level API whose only output channel is yielding (entry, index), so a block is mandatory.

Solutions

  1. Always pass a block: CanvasUnzip.each_entry(path) { |entry, i| ... }.
  2. If wrapping, forward the block explicitly: def self.each_entry(path, &block); CanvasUnzip.each_entry(path, &block); end.
  3. Prefer extract_archive with a block, or compute_uncompressed_size, for common use cases.

Example fix

// before
CanvasUnzip.each_entry(path) # ArgumentError: no block given
// after
names = []
CanvasUnzip.each_entry(path) { |entry, _i| names << entry.name }
Defensive patterns

Strategy: validation

Validate before calling

raise ArgumentError, "block required" unless block_given?

Type guard

def each_entry_safe(path, &block)
  return enum_for(:each_entry_safe, path) unless block
  CanvasUnzip.each_entry(path, &block)
end

Try / catch

begin
  CanvasUnzip.each_entry(path) { |e, i| handle(e, i) }
rescue ArgumentError => e
  raise e unless e.message == "no block given"
  raise UsageError, "each_entry requires a block"
end

Prevention

When it happens

Trigger: Calling CanvasUnzip.each_entry('archive.zip') directly without a block; forwarding methods that drop the implicit block (define_method without &block, method_missing chains losing the block).

Common situations: Using each_entry for its return value (it's for streaming); wrapping each_entry in a helper that forgot &block; count/size utilities calling it without a pass-through block.

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/7179b2dfed54aace. Report an issue: GitHub.

Appendix: source

Thrown at gems/canvas_unzip/lib/canvas_unzip.rb:118

            bytes_left -= size
            raise SizeLimitExceeded if bytes_left < 0
          end

          files_left -= 1
        rescue DestinationFileExists
          add_warning(warnings, entry, :already_exists)
        rescue Zip::CompressionMethodError
          add_warning(warnings, entry, :unknown_compression_method)
        rescue Errno::ENAMETOOLONG
          add_warning(warnings, entry, :filename_too_long)
        end
      end
    end
    warnings
  end

  def self.each_entry(archive_filename)
    raise ArgumentError, "no block given" unless block_given?

    file = File.open(archive_filename)
    mime_type = File.mime_type(file)

    # on some systems `file` fails to recognize a zip file with no entries; fall back on using the extension
    mime_type = File.mime_type(archive_filename) if mime_type == "application/octet-stream"

    if ["application/x-gzip", "application/gzip"].include? mime_type
      file = Zlib::GzipReader.new(file)
      mime_type = "application/x-tar" # it may not actually be a tar though, so rescue if there's a problem
    end

    case mime_type
    when "application/zip"
      Zip::File.open(file) do |zipfile|
        zipfile.entries.each_with_index do |zip_entry, index|
          yield(Entry.new(zip_entry), index)
        end

View on GitHub (pinned to 1c9f0bb801)