jordansissel/fpm · error · FPM::Util::UnsupportedSpecialFile

File is device which fpm doesn't know how to copy (#{File.ft

Error message

File is device which fpm doesn't know how to copy (#{File.ftype(src)}): #{src}

What it means

The staging copy logic explicitly refuses characterSpecial and blockSpecial files — device nodes — because packaging device files is almost never intended and would require privileged mknod at install time. The error names the File.ftype and the source path so you can locate the node.

Source

Thrown at lib/fpm/util.rb:358

      st.ftype
    end

    case filetype
    when 'fifo'
      if File.respond_to?(:mkfifo)
        File.mkfifo(dst)
      elsif program_exists?("mkfifo")
        safesystem("mkfifo", dst)
      else
        raise NamedPipeCannotBeCopied("Unable to copy. Cannot find program 'mkfifo' and Ruby is missing the 'File.mkfifo' method: #{src}")
      end
    when 'socket'
      require "socket"
      # In 2019, Ruby's FileUtils added this as a way to "copy" a unix socket.
      # Reference: https://github.com/ruby/fileutils/pull/36/files
      UNIXServer.new(dst).close()
    when 'characterSpecial', 'blockSpecial'
      raise  UnsupportedSpecialFile.new("File is device which fpm doesn't know how to copy (#{File.ftype(src)}): #{src}")
    when 'directory'
      FileUtils.mkdir(dst) unless File.exist? dst
    when 'hardlink'
      # Handle hardlinks
      # if the file with the same dev and inode has been copied already.
      # hard link it's copy to `dst`, otherwise make an actual copy
      known_entry = copied_entries[[st.dev, st.ino]]
      if known_entry
        FileUtils.ln(known_entry, dst)
        logger.debug("Copying hardlink", :src => src, :dst => dst, :link => known_entry)
      else
        FileUtils.copy_entry(src, dst, preserve, false,
                             remove_destination)
        copied_entries[[st.dev, st.ino]] = dst
      end
    else
      # Normal file, just copy it.
      FileUtils.copy_entry(src, dst, preserve, false,

View on GitHub (pinned to b6d77ba72a)

Solutions

  1. Remove the device nodes from the source tree — note that fpm's --exclude prunes after staging, so it cannot preempt this raise during the initial -s dir copy
  2. Point fpm at a sanitized copy: tar -C rootfs --exclude='./dev/*' -cf - . | tar -C clean -xf -, then package clean/
  3. If devices are genuinely required, create them in a post-install script with mknod instead of the payload

Example fix

# before
fpm -s dir -t deb ./rootfs   # rootfs/dev holds device nodes => UnsupportedSpecialFile

# after
mkdir clean && tar -C rootfs --exclude='./dev/*' -cf - . | tar -C clean -xf -
fpm -s dir -t deb ./clean
Defensive patterns

Strategy: type-guard

Type guard

require 'find'

def device_nodes?(root)
  found = false
  Find.find(root) do |f|
    st = File.lstat(f) rescue next
    if st.chardev? || st.blockdev?
      found = true
      break
    end
  end
  found
end

abort 'input tree contains device nodes' if device_nodes?('./rootfs')

Try / catch

begin
  pkg.input('./rootfs')
rescue FPM::Util::UnsupportedSpecialFile => e
  warn "device node in input (#{e.message}); sanitize the tree and retry"
end

Prevention

When it happens

Trigger: Running 'fpm -s dir' over a tree that contains device nodes: an extracted container rootfs or chroot with a populated /dev, a tarball that preserved device entries, or test fixtures created with mknod.

Common situations: Packaging a whole rootfs/chroot for redistribution; importing VM/container images whose /dev was populated; backups of / fed straight into fpm.

Related errors


AI-assisted analysis of jordansissel/fpm@b6d77ba72a (2026-08-21). Data as JSON: /api/errors/e1258360a8c827ae. Report an issue: GitHub.