hashicorp/vagrant · error · Vagrant::Plugin::V1::InvalidCommandName

Commands can only contain letters, numbers, and hyphens

Error message

Commands can only contain letters, numbers, and hyphens

What it means

Plugin DSL validation in the v1 API: a name passed to the `command "name"` helper must match /^[-a-z0-9]+$/i — letters, digits, and hyphens only (lib/vagrant/plugin/v1/plugin.rb:86). Underscores, colons, spaces, or slashes raise Vagrant::Plugin::V1::Errors::InvalidCommandName at plugin definition time, i.e. when the plugin loads, before any command runs.

Source

Thrown at lib/vagrant/plugin/v1/plugin.rb:86

          # Return the list if we don't have a block
          return hooks if !block_given?

          # Otherwise add the block to the list of hooks for this action.
          hooks << block
        end

        # Defines additional command line commands available by key. The key
        # becomes the subcommand, so if you register a command "foo" then
        # "vagrant foo" becomes available.
        #
        # @param [String] name Subcommand key.
        def self.command(name=UNSET_VALUE, &block)
          data[:command] ||= Registry.new

          if name != UNSET_VALUE
            # Validate the name of the command
            if name.to_s !~ /^[-a-z0-9]+$/i
              raise InvalidCommandName, "Commands can only contain letters, numbers, and hyphens"
            end

            # Register a new command class only if a name was given.
            data[:command].register(name.to_sym, &block)
          end

          # Return the registry
          data[:command]
        end

        # Defines additional communicators to be available. Communicators
        # should be returned by a block passed to this method. This is done
        # to ensure that the class is lazy loaded, so if your class inherits
        # from or uses any Vagrant internals specific to Vagrant 1.0, then
        # the plugin can still be defined without breaking anything in future
        # versions of Vagrant.
        #
        # @param [String] name Communicator name.

View on GitHub (pinned to 35f3160f4a)

Solutions

  1. Rename the subcommand to a hyphenated lowercase form: my_command -> my-command
  2. Keep the gem name unchanged — only the subcommand key is validated
  3. For grouping, use a hyphenated prefix scheme (myplug-list, myplug-add) instead of colons or underscores

Example fix

# before
class MyPlugin < Vagrant.plugin("1")
  name "my plugin"
  command "my_command" do
    require File.expand_path("../command", __FILE__)
    Command
  end
end

# after
class MyPlugin < Vagrant.plugin("1")
  name "my plugin"
  command "my-command" do
    require File.expand_path("../command", __FILE__)
    Command
  end
end
Defensive patterns

Strategy: type-guard

Validate before calling

COMMAND_NAME_RE = /\A[-a-zA-Z0-9]+\z/
raise "invalid command name '#{name}'" unless name.to_s.match?(COMMAND_NAME_RE)

Type guard

# Ruby guard for the Vagrant command DSL (v1 and v2)
COMMAND_NAME_RE = /\A[-a-zA-Z0-9]+\z/
def valid_command_name?(name)
  name.is_a?(String) && !name.empty? && name.match?(COMMAND_NAME_RE)
end

Try / catch

begin
  command "my-command" do
    require_relative "command"
    Command
  end
rescue Vagrant::Plugin::V1::Errors::InvalidCommandName => e
  warn e.message # "Commands can only contain letters, numbers, and hyphens"
end

Prevention

When it happens

Trigger: A plugin's `class MyPlugin < Vagrant.plugin("1")` block calling `command "my_command"`, `command "ns:cmd"`, or `command "my command" — the register call never happens because the name check fails first.

Common situations: Plugin authors porting rake-style namespaced names (colons) or Ruby underscore conventions into subcommand names; copying class names with `::` into the command key.

Related errors


AI-assisted analysis of hashicorp/vagrant@35f3160f4a (2026-08-21). Data as JSON: /api/errors/32d4fed9fdd267c0. Report an issue: GitHub.