jordansissel/fpm · error · FPM::Package::InvalidArgument

Options file already loaded once. Refusing to load a second

Error message

Options file already loaded once. Refusing to load a second time. Maybe a file tries to load itself? Path: #{path}

What it means

load_options records every --fpm-options-file path in @loaded_files; encountering the same path a second time raises FPM::Package::InvalidArgument to break infinite recursion. In practice an options file includes itself, or two files include each other in a cycle.

Source

Thrown at lib/fpm/command.rb:610

    logger.warn("Additional options: #{flags.join " "}") if flags.size > 0
    logger.warn("Additional arguments: #{args.join " "}") if args.size > 0

    ARGV.unshift(*flags)
    ARGV.push(*args)

    super(run_args)
  rescue FPM::Package::InvalidArgument => e
    logger.error("Invalid package argument: #{e}")
    return 1
  end # def run

  def load_options(path)
    @loaded_files ||= []

    if @loaded_files.include?(path)
      #logger.error("Options file was already loaded once. Refusing to load a second time.", :path => path)
      raise FPM::Package::InvalidArgument, "Options file already loaded once. Refusing to load a second time. Maybe a file tries to load itself? Path: #{path}"
    end

    if !File.exist?(path)
      logger.fatal("Cannot load options from file because the file doesn't exist.", :path => path)
    end

    if !File.readable?(path)
      logger.fatal("Cannot load options from file because the file isn't readable.", :path => path)
    end

    @loaded_files << path

    logger.info("Loading flags from file", :path => path)

    # Safety check, abort if the file is huge. Arbitrarily chosen limit is 100kb
    stat = File.stat(path)
    max = 100 * 1024
    if stat.size > max

View on GitHub (pinned to b6d77ba72a)

Solutions

  1. Remove the self-referencing --fpm-options-file line from the file
  2. Restructure so one base file holds shared flags and only leaf configs include it (keep the include graph a tree)
  3. Grep both sides of a cross-include pair and drop one direction

Example fix

# before: fpm.conf contains its own path
--fpm-options-file ./fpm.conf
--log debug

# after: fpm.conf
--log debug
Defensive patterns

Strategy: validation

Validate before calling

path = File.expand_path('fpm.conf')
File.readlines(path).each do |line|
  args = line.split
  if (i = args.index('--fpm-options-file')) && args[i + 1] && File.expand_path(args[i + 1]) == path
    abort 'options file includes itself; remove the self-referencing line'
  end
end

Try / catch

begin
  FPM::Command.run(%w[--fpm-options-file fpm.conf --version])
rescue FPM::Package::InvalidArgument => e
  abort "options-file cycle detected: #{e.message}"
end

Prevention

When it happens

Trigger: An options file containing its own path (--fpm-options-file ./fpm.conf listed inside fpm.conf); file A including B while B includes A; the same file referenced twice on one command line.

Common situations: Copy-pasting example configs that self-reference; refactoring shared flags into mutually-including files; generated configs that append their own loader line on each run.

Related errors


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