BetterErrors/better_errors · error · ArgumentError

Expected editor to be a valid editor key, a format string or

Error message

Expected editor to be a valid editor key, a format string or a callable.

What it means

BetterErrors.editor= configures which editor opens when you click a file/line link on the error page, and it only accepts three input shapes: a Symbol naming a supported editor preset (:atom, :emacs, :emacsclient, :idea, :macvim, :mvim, :rubymine, :sublime, :subl, :st, :textmate, :txmt, :tm, :vscode, :code, :vscodium, :codium), a String containing a URL format template with %{file}/%{line} placeholders, or any object responding to #call (a Proc/lambda taking (file, line)). The setter dispatches on those exact types; anything else — nil, a Hash, an Integer, an editor object — falls into the else branch and this ArgumentError is raised at configuration time, before any error page is rendered.

Source

Thrown at lib/better_errors.rb:115

  #   a suitable substitute.)
  #
  #   @example
  #     BetterErrors.editor = proc { |file, line|
  #       "my-editor://open?url=#{URI.encode_www_form_component file}&line=#{line}"
  #     }
  #
  #   @param [Proc] proc
  #
  def self.editor=(editor)
    if editor.is_a? Symbol
      @editor = Editor.editor_from_symbol(editor)
      raise(ArgumentError, "Symbol #{editor} is not a symbol in the list of supported errors.") unless editor
    elsif editor.is_a? String
      @editor = Editor.for_formatting_string(editor)
    elsif editor.respond_to? :call
      @editor = Editor.for_proc(editor)
    else
      raise ArgumentError, "Expected editor to be a valid editor key, a format string or a callable."
    end
  end

  # Enables experimental Pry support in the inline REPL
  #
  # If you encounter problems while using Pry, *please* file a bug report at
  # https://github.com/BetterErrors/better_errors/issues
  def self.use_pry!
    REPL::PROVIDERS.unshift const: :Pry, impl: "better_errors/repl/pry"
  end

  # Automatically sniffs a default editor preset based on the EDITOR
  # environment variable.
  #
  # @return [Symbol]
  def self.default_editor
    Editor.default_editor
  end

View on GitHub (pinned to fde3b7025d)

Solutions

  1. Pass one of the supported Symbol presets: BetterErrors.editor = :vscode (or :subl, :atom, :emacs, :idea, :macvim, :rubymine, :textmate, :vscodium).
  2. If you need a custom editor, pass a format String with %{file} and %{line} placeholders: BetterErrors.editor = "myeditor --goto %{file}:%{line}".
  3. For dynamic logic, pass a callable: BetterErrors.editor = ->(file, line) { "myeditor #{file} +#{line}" } (must respond to #call and take file, line).
  4. If the value comes from ENV, guard it first: BetterErrors.editor = ENV["EDITOR"] || :vscode — a nil env var otherwise hits the else branch and raises.
  5. Check the value's type/respond_to?(:call) before assigning when the config is user-supplied (see validation code below).

Example fix

# before
BetterErrors.editor = ENV["MY_EDITOR"]          # nil in CI -> ArgumentError
BetterErrors.editor = { editor: :vscode }       # Hash -> ArgumentError

# after
BetterErrors.editor = ENV["MY_EDITOR"] || :vscode
# or, for a custom command:
BetterErrors.editor = "code --goto %{file}:%{line}"
# or, for a callable:
BetterErrors.editor = ->(file, line) { "code --goto #{file}:#{line}" }
Defensive patterns

Strategy: validation

Validate before calling

SUPPORTED_EDITOR_SYMBOLS = [
  :atom, :emacs, :emacsclient, :idea, :macvim, :mvim, :rubymine,
  :sublime, :subl, :st, :textmate, :txmt, :tm,
  :vscode, :code, :vscodium, :codium
].freeze

# Returns true if the value can be safely assigned to BetterErrors.editor=
def valid_better_errors_editor?(value)
  return true if value.is_a?(String)                       # format string
  return true if value.respond_to?(:call)                  # proc/lambda
  value.is_a?(Symbol) && SUPPORTED_EDITOR_SYMBOLS.include?(value)
end

# Usage in an initializer:
raw = ENV["BETTER_ERRORS_EDITOR_KEY"]
editor = raw&.to_sym
BetterErrors.editor = editor if valid_better_errors_editor?(editor)

Type guard

# Ruby has no static types; use a respond_to/is_a? guard before assigning:
def set_better_errors_editor(value)
  unless value.is_a?(Symbol) || value.is_a?(String) || value.respond_to?(:call)
    raise ArgumentError, "editor must be a Symbol, format String, or #call-able, got #{value.class}"
  end
  BetterErrors.editor = value
end

Try / catch

begin
  BetterErrors.editor = configured_editor
rescue ArgumentError => e
  warn "better_errors: ignoring invalid editor config (#{e.message}); falling back to default"
  BetterErrors.editor = :vscode # safe default preset
end

Prevention

When it happens

Trigger: Calling BetterErrors.editor= with a value that is neither Symbol, String, nor #call-responding: e.g. BetterErrors.editor = nil (e.g. ENV["EDITOR"] returned nil and was passed straight through), BetterErrors.editor = 42, BetterErrors.editor = { editor: :vscode } (options Hash instead of the value), or passing a custom editor object that lacks a #call(file, line) method. Typical call sites are an initializer (config/initializers/better_errors.rb), a Gemfile-adjacent setup script, or a test/spec setup block.

Common situations: Passing an untrusted env var through without a nil check (EDITOR/BETTER_ERRORS_EDITOR unset in CI); upgrading from an older better_errors version where the accepted shapes differed and a string like "vscode" used to be treated as a preset name but is now parsed as a format string; wrapping the editor in a Hash or Struct instead of a lambda when migrating from a string template to a callable; copy-pasting config snippets written for a different library (e.g. pry-byebug style options) into BetterErrors.editor=.

Understand the failure class

Background: Invalid argument type errors: "must be of type string", "expected X, got Y", and ERR_INVALID_ARG_TYPE explained — this error's family across 15 libraries.

Related errors


AI-assisted analysis of BetterErrors/better_errors@fde3b7025d (2026-08-21). Data as JSON: /api/errors/7bfa874cefa55703. Report an issue: GitHub.