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
- 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
- Point fpm at a sanitized copy: tar -C rootfs --exclude='./dev/*' -cf - . | tar -C clean -xf -, then package clean/
- 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
- Never package a populated /dev; exclude it when exporting rootfs images
- Sanitize extracted tars with tar --exclude='./dev/*' before -s dir
- Add a device-node scan as a fast CI precheck instead of failing mid-build
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
- Parent directory does not exist: #{File.dirname(output_path)
- #{self.class.name} does not yet support reading #{self.type}
- #{self.class.name} does not yet support creating #{self.type
- File already exists, refusing to continue: #{output_path}
- I don't know how to build #{name}. No Makefile.PL nor Build.
AI-assisted analysis of jordansissel/fpm@b6d77ba72a (2026-08-21).
Data as JSON: /api/errors/e1258360a8c827ae.
Report an issue: GitHub.