puppetlabs/puppet · error · CommandlineError

file or url for option '%{arg}' cannot be opened: %{value0}

Error message

file or url for option '%{arg}' cannot be opened: %{value0}

What it means

Puppet's vendored Trollop option parser supports options whose value is an IO source (handled by parse_io_parameter): '-' or 'stdin' maps to $stdin, anything else is treated as a file path or URL and opened via URI.parse(param).open. If that open fails with a SystemCallError (Errno::ENOENT for a missing file, Errno::EACCES for an unreadable one, EISDIR, etc.), Trollop wraps it into a CommandlineError naming the option and the underlying errno message. It signals bad command-line input, not a bug in the program.

Source

Thrown at lib/puppet/util/command_line/trollop.rb:678

      param.to_i
    end

    def parse_float_parameter param, arg
      raise CommandlineError, _("option '%{arg}' needs a floating-point number") % { arg: arg } unless param =~ FLOAT_RE

      param.to_f
    end

    def parse_io_parameter param, arg
      case param
      when /^(stdin|-)$/i; $stdin
      else
        require 'open-uri'
        begin
          URI.parse(param).open
        rescue SystemCallError => e
          raise CommandlineError, _("file or url for option '%{arg}' cannot be opened: %{value0}") % { arg: arg, value0: e.message }, e.backtrace
        end
      end
    end

    def collect_argument_parameters args, start_at
      params = []
      pos = start_at
      while args[pos] && args[pos] !~ PARAM_RE && !@stop_words.member?(args[pos])
        params << args[pos]
        pos += 1
      end
      params
    end

    def resolve_default_short_options
      @order.each do |type, name|
        next unless type == :opt

View on GitHub (pinned to e227c27540)

Solutions

  1. Verify the path exists and is readable before running the command (test -r), and fix any typo
  2. Use an absolute path so the value does not depend on the current working directory
  3. Check read permission for the invoking user, or run with the privileges the file requires
  4. If the option supports it, pipe the content via '-' / stdin instead of naming a file

Example fix

# before
puppet mytool --data ./conf/data.yaml
# file does not exist => CommandlineError: file or url for option '--data' cannot be opened: No such file or directory

# after
test -r /etc/puppetlabs/code/data.yaml && puppet mytool --data /etc/puppetlabs/code/data.yaml
# or read from stdin
cat data.yaml | puppet mytool --data -
Defensive patterns

Strategy: validation

Validate before calling

value = ARGV[ARGV.index('--data') + 1] if ARGV.index('--data')
if value && value !~ /\A(-|stdin)\z/i && value !~ %r{\A\w+://}
  abort "#{value} is not a readable file" unless File.readable?(value)
end

Try / catch

begin
  Trollop.options { opt :data, type: :io }
rescue Puppet::Util::CommandLine::Trollop::CommandlineError => e
  warn "Bad input: #{e.message}"
  exit 1
end

Prevention

When it happens

Trigger: Running a Puppet face/application whose option is declared with an IO type and passing a path that does not exist, is a directory, or is not readable by the current user. A plain local path like /etc/missing.conf makes URI.parse(...).open raise Errno::ENOENT, which is a SystemCallError and reaches this rescue clause.

Common situations: Typos or stale paths in the argument (file moved or renamed), relative paths resolved from an unexpected current working directory, running the command as a user without read permission on the file, and scripts written on one host being run on another where the file is absent.

Related errors


AI-assisted analysis of puppetlabs/puppet@e227c27540 (2026-08-21). Data as JSON: /api/errors/3fa73f881a5c4fd9. Report an issue: GitHub.