{"record":{"id":"81fd4a7c02c0ebb8","repo":"jordansissel/fpm","slug":"invalid-systemd-unit-file-extension-extname-e","errorCode":null,"errorMessage":"Invalid systemd unit file extension: #{extname}. Expected one of: #{possible_extensions_str}","messagePattern":"Invalid systemd unit file extension: #(.+?)\\. Expected one of: #(.+?)","errorType":"validation","errorClass":"FPM::InvalidPackageConfiguration","httpStatus":null,"severity":"error","filePath":"lib/fpm/package/deb.rb","lineNumber":574,"sourceCode":"        \".scope\",\n    ]\n\n    attributes[:deb_systemd] = []\n    attributes.fetch(:deb_systemd_list, []).each do |systemd|\n      name = File.basename(systemd)\n      extname = File.extname(name)\n\n      name_with_extension = if extname.empty?\n                              \"#{name}.service\"\n                            elsif systemd_file_extensions.include?(extname)\n                              name\n                            else\n                              # Mutating the array is fine as we raise directly after, and the array will be re-initialised next time\n                              # this method is called. If this branch is changed in the future so as not to diverge, care should be\n                              # taken to ensure that this mutated version of the array is only used for generating the error message.\n                              systemd_file_extensions[-1] = systemd_file_extensions[-1].prepend(\"or \")\n                              possible_extensions_str = systemd_file_extensions.join(\", \")\n                              raise FPM::InvalidPackageConfiguration,\n                                \"Invalid systemd unit file extension: #{extname}. Expected one of: #{possible_extensions_str}\"\n                            end\n\n      dest_systemd = staging_path(File.join(attributes[:deb_systemd_path], \"#{name_with_extension}\"))\n      mkdir_p(File.dirname(dest_systemd))\n      FileUtils.cp(systemd, dest_systemd)\n      File.chmod(0644, dest_systemd)\n\n      attributes[:deb_systemd] << name_with_extension\n    end\n\n    if script?(:before_upgrade) or script?(:after_upgrade) or attributes[:deb_systemd].any?\n      puts \"Adding action files\"\n      if script?(:before_install) or script?(:before_upgrade)\n        scripts[:before_install] = template(\"deb/preinst_upgrade.sh.erb\").result(binding)\n      end\n      if script?(:before_remove) or not attributes[:deb_systemd].empty?\n        scripts[:before_remove] = template(\"deb/prerm_upgrade.sh.erb\").result(binding)","sourceCodeStart":556,"sourceCodeEnd":592,"githubUrl":"https://github.com/jordansissel/fpm/blob/b6d77ba72a560b687723376a0e5115c3a92634ad/lib/fpm/package/deb.rb#L556-L592","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"# before\nfpm -s dir -t deb -n foo --deb-systemd myapp.service.in .\n# -> Invalid systemd unit file extension: .in\n\n# after\nerb myapp.service.in > myapp.service\nfpm -s dir -t deb -n foo --deb-systemd myapp.service .","handlingStrategy":"validation","validationCode":"VALID_EXT = %w[.service .socket .device .mount .automount .swap .target .path .timer]\n\nsystemd_units.each do |path|\n  ext = File.extname(path)\n  next if ext.empty? || VALID_EXT.include?(ext)\n  abort \"#{path}: not a systemd unit extension (#{VALID_EXT.join(' ')})\"\nend","typeGuard":"def valid_systemd_unit?(path)\n  ext = File.extname(path)\n  ext.empty? || %w[.service .socket .device .mount .automount .swap .target .path .timer].include?(ext)\nend","tryCatchPattern":"begin\n  pkg.output(out)\nrescue FPM::InvalidPackageConfiguration => e\n  raise unless e.message =~ /Invalid systemd unit file extension/\n  pkg.attributes[:deb_systemd] = pkg.attributes[:deb_systemd].select { |u| valid_systemd_unit?(u) }\n  retry\nend","preventionTips":["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"],"tags":["fpm","deb","systemd","file-extension","validation","packaging"],"backgroundTag":"invalid-file-extension","analyzedSha":"b6d77ba72a560b687723376a0e5115c3a92634ad","analyzedAt":"2026-08-21T16:39:02.570Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}