jordansissel/fpm · error · FPM::InvalidPackageConfiguration
Invalid systemd unit file extension: #{extname}. Expected on
Error message
Invalid systemd unit file extension: #{extname}. Expected one of: #{possible_extensions_str} What it means
Each file passed via --deb-systemd is checked by extension. Files with no extension get '.service' appended; files with an extension must be one of the recognized systemd unit types (.service, .socket, .device, .mount, .automount, .swap, .target, .path, .timer). Any other extension raises FPM::InvalidPackageConfiguration listing the allowed set.
Source
Thrown at lib/fpm/package/deb.rb:574
".scope",
]
attributes[:deb_systemd] = []
attributes.fetch(:deb_systemd_list, []).each do |systemd|
name = File.basename(systemd)
extname = File.extname(name)
name_with_extension = if extname.empty?
"#{name}.service"
elsif systemd_file_extensions.include?(extname)
name
else
# Mutating the array is fine as we raise directly after, and the array will be re-initialised next time
# this method is called. If this branch is changed in the future so as not to diverge, care should be
# taken to ensure that this mutated version of the array is only used for generating the error message.
systemd_file_extensions[-1] = systemd_file_extensions[-1].prepend("or ")
possible_extensions_str = systemd_file_extensions.join(", ")
raise FPM::InvalidPackageConfiguration,
"Invalid systemd unit file extension: #{extname}. Expected one of: #{possible_extensions_str}"
end
dest_systemd = staging_path(File.join(attributes[:deb_systemd_path], "#{name_with_extension}"))
mkdir_p(File.dirname(dest_systemd))
FileUtils.cp(systemd, dest_systemd)
File.chmod(0644, dest_systemd)
attributes[:deb_systemd] << name_with_extension
end
if script?(:before_upgrade) or script?(:after_upgrade) or attributes[:deb_systemd].any?
puts "Adding action files"
if script?(:before_install) or script?(:before_upgrade)
scripts[:before_install] = template("deb/preinst_upgrade.sh.erb").result(binding)
end
if script?(:before_remove) or not attributes[:deb_systemd].empty?
scripts[:before_remove] = template("deb/prerm_upgrade.sh.erb").result(binding)View on GitHub (pinned to b6d77ba72a)
Solutions
- Render templates first and pass the final unit file with a valid extension (foo.service)
- Rename files that are genuinely units but carry a wrong extension
- Drop the extension entirely so fpm appends .service (--deb-systemd myapp yields myapp.service)
- Remove non-unit files from the --deb-systemd list and ship them via -s dir input instead
Example fix
# before fpm -s dir -t deb -n foo --deb-systemd myapp.service.in . # -> Invalid systemd unit file extension: .in # after erb myapp.service.in > myapp.service fpm -s dir -t deb -n foo --deb-systemd myapp.service .
Defensive patterns
Strategy: validation
Validate before calling
VALID_EXT = %w[.service .socket .device .mount .automount .swap .target .path .timer]
systemd_units.each do |path|
ext = File.extname(path)
next if ext.empty? || VALID_EXT.include?(ext)
abort "#{path}: not a systemd unit extension (#{VALID_EXT.join(' ')})"
end Type guard
def valid_systemd_unit?(path) ext = File.extname(path) ext.empty? || %w[.service .socket .device .mount .automount .swap .target .path .timer].include?(ext) end
Try / catch
begin
pkg.output(out)
rescue FPM::InvalidPackageConfiguration => e
raise unless e.message =~ /Invalid systemd unit file extension/
pkg.attributes[:deb_systemd] = pkg.attributes[:deb_systemd].select { |u| valid_systemd_unit?(u) }
retry
end Prevention
- Render .erb/.in unit templates before passing them to --deb-systemd
- Validate unit filenames against the systemd extension list in packaging scripts
- Drop the extension for plain services; fpm appends .service automatically
When it happens
Trigger: Passing --deb-systemd with a file like myapp.init, unit.txt, or myapp.service.in: the extension is present but not in the whitelist, so fpm refuses rather than shipping a file systemd would ignore. Only exact extensions from the list are accepted.
Common situations: SysV-to-systemd migration scripts passing .init or .sh files; template files (.service.erb/.service.in) passed directly instead of rendered output; typos like .servcie; documentation files accidentally included in the unit list.
Related errors
- The version looks invalid for Debian packages. Debian versio
- Found invalid Provides field values (#{provide.inspect}). Th
- deb compression value of '#{value}' is invalid. Must be one
- Missing control.tar in deb source package #{package}
- Unknown compression type '#{compression}' for control.tar in
AI-assisted analysis of jordansissel/fpm@b6d77ba72a (2026-08-21).
Data as JSON: /api/errors/81fd4a7c02c0ebb8.
Report an issue: GitHub.