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

'#{editor}' failed (exit code #{$?.exitstatus}) Full command

Error message

'#{editor}' failed (exit code #{$?.exitstatus}) Full command was: #{command}

What it means

With --edit, fpm writes the generated spec/metadata to a temp file and launches an editor via system(): the program from $FPM_EDITOR, else $EDITOR, else 'vi'. If that editor process exits non-zero, FPM::Util::ProcessFailed is raised with the exit status and the full command line. The failure belongs to the editor process, not to fpm's packaging logic.

Source

Thrown at lib/fpm/package.rb:372

  def to_s(fmt=nil)
    fmt = "NAME.EXTENSION" if fmt.nil?
    return fmt.gsub("ARCH", to_s_arch) \
      .gsub("NAME",         to_s_name) \
      .gsub("FULLVERSION",  to_s_fullversion) \
      .gsub("VERSION",      to_s_version) \
      .gsub("ITERATION",    to_s_iteration) \
      .gsub("EPOCH",        to_s_epoch) \
      .gsub("TYPE",         to_s_type) \
      .gsub("EXTENSION",    to_s_extension)
  end # def to_s

  def edit_file(path)
    editor = ENV['FPM_EDITOR'] || ENV['EDITOR'] || 'vi'
    logger.info("Launching editor", :file => path)
    command = "#{editor} #{Shellwords.escape(path)}"
    system("#{editor} #{Shellwords.escape(path)}")
    if !$?.success?
      raise ProcessFailed.new("'#{editor}' failed (exit code " \
                              "#{$?.exitstatus}) Full command was: " \
                              "#{command}");
    end

    if File.size(path) == 0
      raise "Empty file after editing: #{path.inspect}"
    end
  end # def edit_file

  # This method removes excluded files from the staging_path. Subclasses can
  # remove the files during the input phase rather than deleting them here
  def exclude
    return if attributes[:excludes].nil?

    if @attributes.include?(:prefix)
      installdir = staging_path(@attributes[:prefix])
    else
      installdir = staging_path

View on GitHub (pinned to b6d77ba72a)

Solutions

  1. Export a working editor before running: FPM_EDITOR=vim (and confirm 'which vim' succeeds)
  2. Drop --edit in non-interactive contexts and supply fields via flags (--description, --after-install, --template-scripts, ...)
  3. If the editor is a wrapper, ensure it exits 0 on success; test with '$FPM_EDITOR /tmp/probe'
  4. For scripted edits, point FPM_EDITOR at a non-interactive filter (e.g. a sed-based wrapper)

Example fix

# before (CI job, no TTY)
fpm --edit -s dir -t deb ./app   # vi fails without a terminal => ProcessFailed

# after: no interactive edit; set fields explicitly
FPM_EDITOR=vim fpm --edit -s dir -t deb ./app   # local, interactive use only
fpm -s dir -t deb --description 'My app' ./app  # CI-safe
Defensive patterns

Strategy: validation

Validate before calling

editor = (ENV['FPM_EDITOR'] || ENV['EDITOR'] || 'vi').split(' ').first
unless system('which', editor, out: File::NULL, err: File::NULL)
  abort "editor '#{editor}' not found; set FPM_EDITOR or drop --edit"
end

Try / catch

begin
  # invoking fpm with --edit programmatically
rescue FPM::Util::ProcessFailed => e
  abort "editor failed: #{e.message}"  # includes exit code and full command
end

Prevention

When it happens

Trigger: Running 'fpm --edit ...' where FPM_EDITOR/EDITOR points to a missing or crashing binary; a headless/CI shell with no TTY so vi exits with an error; the editor being killed or quitting with a non-zero status.

Common situations: CI pipelines that inherited --edit from a local alias; EDITOR set to a GUI editor needing a display; wrapper-script editors whose last command returns non-zero; minimal containers where even the default vi is absent.

Related errors


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