There's a lot of open issues
RuboCop cops that check raised exception messages start with a lowercase letter and do not end with a period, consistent with Ruby's own core and standard library exceptions.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
 Dependencies

Runtime

~> 1.72
 Project Readme

rubocop-exception_messages

Ruby Coverage Status

RuboCop cops that standardize the style of raised exception messages, consistent with Ruby's own core and standard library exceptions (e.g. TypeError: no implicit conversion from nil to integer, ArgumentError: wrong number of arguments).

Table of Contents

  • Rationale
  • Installation
  • Cops
    • ExceptionMessages/Casing
    • ExceptionMessages/Punctuation
    • ExceptionMessages/RedundantExceptionName
    • ExceptionMessages/QuoteStyle
    • ExceptionMessages/RequireMessage
    • ExceptionMessages/NoGenericMessage
  • Contributing
  • Copyright and License

Rationale

Ruby's built-in exceptions never capitalize or punctuate their messages. This reads naturally when Ruby prints the exception class name, a colon, and the message together in a backtrace (ArgumentError: block is required, not ArgumentError: Block is required.). These cops help keep custom raise messages consistent with that convention.

Installation

Add to your Gemfile:

group :development do
  gem "rubocop-exception_messages", require: false
end

Then require it in your .rubocop.yml:

plugins:
  - rubocop-exception_messages

Cops

All cops recognize both raise Class, "message" and raise Class.new("message") forms. Examples below use the raise Class, "message" form for brevity, except for ExceptionMessages/RequireMessage, where the choice between the two forms matters to the check itself.

ExceptionMessages/Casing

Checks the capitalization of raised exception messages. Defaults to EnforcedStyle: lowercase.

# bad
raise ArgumentError, "Block is required"

# good
raise ArgumentError, "block is required"

Configure EnforcedStyle: uppercase to require the opposite convention instead.

ExceptionMessages/Casing:
  EnforcedStyle: uppercase
# bad
raise ArgumentError, "block is required"

# good
raise ArgumentError, "Block is required"

ExceptionMessages/Punctuation

Checks the trailing punctuation of raised exception messages. Defaults to EnforcedStyle: no_period. A literal ellipsis ("...") is never considered an offense.

# bad
raise ArgumentError, "block is required."

# good
raise ArgumentError, "block is required"

Configure EnforcedStyle: period to require a trailing period instead.

ExceptionMessages/Punctuation:
  EnforcedStyle: period
# bad
raise ArgumentError, "block is required"

# good
raise ArgumentError, "block is required."

Both cops support autocorrection (rubocop -A).

ExceptionMessages/RedundantExceptionName

Checks that raised exception messages do not redundantly repeat the exception class name, since Ruby already prints the class name ahead of the message in a backtrace.

# bad
raise ArgumentError, "ArgumentError: block is required"

# good
raise ArgumentError, "block is required"

ExceptionMessages/QuoteStyle

Checks that interpolated values in raised exception messages are consistently quoted, making it easier to spot where a dynamic value begins and ends in a rendered message. Defaults to EnforcedStyle: backticks.

# bad
raise ArgumentError, "unknown type: #{type}"

# good
raise ArgumentError, "unknown type: `#{type}`"

Configure EnforcedStyle: single_quotes, double_quotes, square_brackets, parentheses, or curly_braces to require a different wrapping instead.

ExceptionMessages/QuoteStyle:
  Enabled: true
  EnforcedStyle: single_quotes
# good
raise ArgumentError, "unknown type: '#{type}'"

Configure EnforcedStyle: custom with Prefix/Suffix for anything else, including a single-sided marker with no closing character.

ExceptionMessages/QuoteStyle:
  Enabled: true
  EnforcedStyle: custom
  Prefix: '?'
# good
raise ArgumentError, "unknown type: ?#{type}"

Prefix/Suffix aren't limited to a single character.

ExceptionMessages/QuoteStyle:
  Enabled: true
  EnforcedStyle: custom
  Prefix: '--'
# good
raise ArgumentError, "unknown type: --#{type}"

Configure EnforcedStyle: none to require interpolated values not be wrapped at all, and flag existing wrapping instead.

ExceptionMessages/QuoteStyle:
  Enabled: true
  EnforcedStyle: none
# bad
raise ArgumentError, "unknown type: `#{type}`"

# good
raise ArgumentError, "unknown type: #{type}"

ExceptionMessages/RequireMessage

Checks that a raised exception is given a message, since a bare raise SomeError produces a backtrace with nothing but the class name to go on.

# bad
raise ArgumentError
raise ArgumentError.new

# good
raise ArgumentError, "block is required"

# good (bare re-raise)
raise

AllowedExceptions exempts exception classes that don't need a message, and defaults to NotImplementedError, since it's conventionally raised bare (e.g. for an abstract method, or a feature unsupported on the current platform).

# good, by default
raise NotImplementedError

RuboCop configuration doesn't merge arrays, it replaces them, so if you configure your own AllowedExceptions, repeat NotImplementedError in the list if you still want it exempted.

ExceptionMessages/RequireMessage:
  Enabled: true
  AllowedExceptions:
    - NotImplementedError
    - MyApp::PluginError
# good
raise NotImplementedError
raise MyApp::PluginError

ExceptionMessages/NoGenericMessage

Checks that a raised exception message provides context by staying within configured character and word limits and not matching a generic message. This catches messages like "invalid" or "failed" by default, as well as messages shorter or longer than the configured limits.

# bad
raise ArgumentError, "invalid"
raise StandardError, "error"
raise RuntimeError, "failed"

# good
raise ArgumentError, "invalid type: `#{type}`"
raise StandardError, "error connecting to the database"
raise RuntimeError, "failed to acquire lock"

MinimumWords defaults to 1. MinimumLength, MaximumLength, and MaximumWords are optional; when configured, messages outside those limits are flagged. GenericMessages is a configurable, case-insensitive list of exact messages that are always flagged, even when they meet the length limits. Entries written as /pattern/flags are treated as regular expressions.

ExceptionMessages/NoGenericMessage:
  Enabled: true
  MaximumLength: 200
  MinimumWords: 1
  MaximumWords: 30
  GenericMessages:
    - bad
    - error
    - failed
    - invalid
    - not found
    - '/^operation (failed|aborted)$/i'

Use Exceptions to override the global settings for a particular exception class. Fully qualified names and short names are supported; per-exception values replace the corresponding global setting.

ExceptionMessages/NoGenericMessage:
  MinimumLength: 1
  GenericMessages:
    - bad
    - error
  Exceptions:
    ArgumentError:
      MinimumLength: 10
      MaximumLength: 100
      MinimumWords: 3
      MaximumWords: 20
      GenericMessages:
        - bad argument
        - '/^invalid argument/i'
# bad
raise ArgumentError, "nope"

To require more context, increase MinimumLength:

ExceptionMessages/NoGenericMessage:
  MinimumLength: 20
  MaximumLength: 200
  MinimumWords: 3
  MaximumWords: 30

Contributing

See CONTRIBUTING.md.

Copyright and License

MIT License, see LICENSE for details.