Common error messages¶
Level: 101 · anyone staring at a message they do not recognise
One line: Twenty-nine errors you will meet in your first month of Ruby, each with the code that raises it, the message Ruby 4.0 prints, what the mistake usually is, the fix, and the lesson that explains it — and the Python error that means the same thing.
This is the library's lookup page. Search it for the message in your terminal. Ruby's messages are stable within a release and are printed in full below (Ruby 3.4 changed how a method name is quoted, from `length' to 'length', so an older tutorial's message differs in that one detail (Not machine-checked here.)); the twin prints only Python's exception types, because CPython rewords its messages from one release to the next. Every row runs; the numbers in the table match the numbers in the output.
Verified output of common_error_messages_rb.rb — regenerated by tools/run_examples.py, never hand-typed.
1. NoMethodError nil.length
undefined method 'length' for nil
2. NoMethodError Vault.new.secret
private method 'secret' called for an instance of Vault
3. NoMethodError "s".zzz
undefined method 'zzz' for an instance of String
4. NameError nope
undefined local variable or method 'nope' for main
5. NameError Nope
uninitialized constant Nope
6. ArgumentError two(1)
wrong number of arguments (given 1, expected 2)
7. ArgumentError kw
missing keyword: :k
8. ArgumentError kw2(z: 1)
unknown keyword: :z
9. ArgumentError [3, nil].sort
comparison of Integer with nil failed
10. TypeError "a" + 1
no implicit conversion of Integer into String
11. TypeError 1 + "a"
String can't be coerced into Integer
12. ZeroDivisionError 1 / 0
divided by 0
13. KeyError {a: 1}.fetch(:x)
key not found: :x
14. IndexError [1, 2, 3].fetch(5)
index 5 outside of array bounds: -3...3
15. FrozenError "abc".freeze << "d"
can't modify frozen String: "abc"
16. StopIteration [].each.next
iteration reached an end
17. LocalJumpError needs_block (def needs_block = yield)
no block given (yield)
18. SystemStackError down (def down = down)
stack level too deep
19. NoMatchingPatternError case 5; in String then 1; end
5: String === 5 does not return true
20. NoMatchingPatternKeyError case {a: 1}; in {name:} then 1; end
{a: 1}: key not found: :name
21. FloatDomainError Float::NAN.to_i
NaN
22. FloatDomainError Float::INFINITY.to_i
Infinity
23. LoadError require "nope_nothing"
cannot load such file -- nope_nothing
24. Errno::ENOENT File.read("missing.txt")
No such file or directory @ rb_sysopen - missing.txt
25. IOError f.close; f.read
closed stream
26. EOFError f.readline at end of file
end of file reached
27. UncaughtThrowError throw :x
uncaught throw :x
28. NotImplementedError raise NotImplementedError
NotImplementedError
29. SyntaxError RubyVM::InstructionSequence.compile("def")
(message not printed: it quotes the source, with carets)
Reading the output¶
| # | Error | The mistake | The fix | Lesson |
|---|---|---|---|---|
| 1 | NoMethodError … for nil |
A value you expected to be set is nil: a hash lookup missed, a method returned nil, an each ran out. |
Find where the nil came from; guard with &., fetch, or a default. |
Safe navigation |
| 2 | NoMethodError private method |
Calling a private method with an explicit receiver. |
Call it without a receiver from inside the class, or make it public. | private means no receiver |
| 3 | NoMethodError … for an instance of String |
The method is misspelt, or the object is not the class you think. | Check obj.class and respond_to?; read the name after undefined method. |
Duck typing and respond_to? |
| 4 | NameError undefined local variable or method |
A bare word that is neither a local variable in scope nor a method: a typo, or a variable assigned only inside a block. | Spell it right; assign it before the block; or add () to make the call explicit. |
Parentheses are optional |
| 5 | NameError uninitialized constant |
A class or module name that is not loaded, misspelt, or outside the lexical scope. | require the file; check the nesting (Foo::Bar). |
Constants and lexical scope |
| 6 | ArgumentError wrong number of arguments |
The call passes too few or too many positionals. | Read (given 1, expected 2) and fix the call or the signature. |
Arguments: positional, keyword and splat |
| 7 | ArgumentError missing keyword |
A required key: parameter was not passed. |
Pass k: value, or give the parameter a default. |
same |
| 8 | ArgumentError unknown keyword |
A keyword the method does not declare — often a hash passed where keywords are expected. | Fix the name, or accept **opts. |
same |
| 9 | ArgumentError comparison … failed |
sort, min or < met two values that do not compare, usually a nil in the data. |
compact first, or sort_by a key that is always comparable. |
Comparable and <=> |
| 10 | TypeError no implicit conversion of Integer into String |
"a" + 1: String#+ wants a String and Ruby does not convert. |
"a" + 1.to_s, or interpolate: "a#{1}". |
Numeric coercion |
| 11 | TypeError String can't be coerced into Integer |
1 + "a": the reverse case, from Integer#+. |
1 + Integer("a") or "a".to_i, depending on whether bad input should raise. |
same |
| 12 | ZeroDivisionError |
Integer division by zero. | Check the divisor first; the numbers chapter covers what float division does instead. | Integer division floors |
| 13 | KeyError |
fetch on a missing key (plain [] returns nil). |
fetch(:x, default), fetch(:x) { … }, or [] if nil is fine. |
Hashes and default values |
| 14 | IndexError |
fetch on an index outside the array (plain [] returns nil). |
fetch(5, default) or []; read the bounds in the message. |
Arrays and negative indexes |
| 15 | FrozenError |
Mutating a frozen object: a literal under # frozen_string_literal: true, a symbol, a Data, something you froze. |
dup it, or build a new value instead of mutating. |
freeze and FrozenError |
| 16 | StopIteration |
next on an exhausted external enumerator. |
loop do … end rescues it for you; or check peek first. |
External enumerators |
| 17 | LocalJumpError |
yield in a method called without a block. |
Check block_given?, or take &block and test it. |
Blocks are not objects |
| 18 | SystemStackError |
Recursion with no base case — or a method that calls itself by accident (def x = x). |
Add the base case; note that a bare rescue will not catch this. |
A bare rescue catches StandardError |
| 19 | NoMatchingPatternError |
case … in with no branch that matches and no else. |
Add an else, or a final in _. |
NoMatchingPatternError |
| 20 | NoMatchingPatternKeyError |
A hash pattern names a key the hash lacks. | Make the key optional in the pattern, or add else; the message names the key. |
same |
| 21 | FloatDomainError NaN |
to_i / round on NaN. |
Test nan? first; find the 0.0 / 0 that produced it. |
Floats and rounding |
| 22 | FloatDomainError Infinity |
to_i / round on Infinity, usually from a float division by zero. |
Test infinite?; check the divisor. |
same |
| 23 | LoadError |
require of a file not on $LOAD_PATH, or a gem that is not installed. |
require_relative for your own files; install the gem; check the name. |
require, require_relative and load |
| 24 | Errno::ENOENT |
Opening a path that does not exist — often relative to the wrong directory. | File.exist?; build the path from __dir__. |
File.open with a block |
| 25 | IOError closed stream |
Reading or writing after close — often after a block-form File.open has ended. |
Do the I/O inside the block; do not keep the handle. | same |
| 26 | EOFError |
readline at end of file (gets returns nil there instead). |
Use gets or each_line, or check eof? first. |
stdin, gets and ARGF |
| 27 | UncaughtThrowError |
throw with no enclosing catch for that tag. |
Wrap the call in catch(:x); a tag is matched by identity, so use a symbol. |
throw and catch |
| 28 | NotImplementedError |
An abstract method left for a subclass to define, and the subclass did not. | Define it; remember a bare rescue will not catch this one either. |
A bare rescue catches StandardError |
| 29 | SyntaxError |
Unbalanced end, do or {, or a keyword out of place. |
ruby -c file.rb to check without running; read the caret line under the error. |
The command-line flags |
Row 29's message is the only one the program does not print: a SyntaxError quotes the offending source with caret markers, a multi-line block rather than a sentence, and this library does not key it. (Not machine-checked here.) The classes in rows 18, 23, 28 and 29 are not StandardErrors, and the hierarchy lesson is where that matters.
Compared with Python¶
The twin runs the counterpart of each row and prints the exception type only. Most rows map to one Python type: NoMethodError and NameError become AttributeError and NameError; Python has no constants, so row 5 is another NameError; rows 6 to 9 are all TypeError, which is Python's answer to every argument-count, keyword and comparison problem — there is no ArgumentError; rows 10 and 11 are TypeError too; rows 13 and 14 are KeyError and IndexError from plain [], because Python has no fetch/[] split — dict.get and slicing are the lenient forms. Row 15 is a TypeError, since a str is immutable rather than frozen (a dataclass(frozen=True) raises FrozenInstanceError instead). Row 17 has no blocks to miss, so the closest thing is calling None. Row 18 is RecursionError — an ordinary Exception, where SystemStackError is not a StandardError. Rows 19 and 20 are the largest difference: a match with no matching case does nothing and raises nothing. Rows 21 and 22 split by kind: int(nan) is ValueError, int(inf) is OverflowError. Rows 23 and 24 are ModuleNotFoundError and FileNotFoundError, both more specific than Ruby's; row 25 is a ValueError (I/O on a closed file); row 26 needs pickle.load on empty input to raise EOFError, because readline at end of file simply returns "". Row 27 has no counterpart, and row 28 is a plain Exception in Python.
Verified output of common_error_messages_py.py — regenerated by tools/run_examples.py, never hand-typed.
1. AttributeError None.length
2. AttributeError Vault().__secret()
3. AttributeError "s".zzz()
4. NameError nope
5. NameError Nope (no constants: the same NameError)
6. TypeError two(1)
7. TypeError kw(z=1) (a missing keyword is the same TypeError)
8. TypeError kw(k=1, z=1)
9. TypeError sorted([3, None])
10. TypeError "a" + 1
11. TypeError 1 + "a"
12. ZeroDivisionError 1 / 0
13. KeyError {"a": 1}["x"]
14. IndexError [1, 2, 3][5]
15. TypeError s = "abc"; s[0] = "d" (str is immutable)
16. StopIteration next(iter([]))
17. TypeError apply(None) (no blocks: a missing callable is None)
18. RecursionError down() (def down(): down())
19. (no error) match 5: case str(): ...
20. (no error) match {"a": 1}: case {"name": _}: ...
21. ValueError int(float("nan"))
22. OverflowError int(float("inf"))
23. ModuleNotFoundError import nope_nothing
24. FileNotFoundError open("missing.txt")
25. ValueError f.read() on a closed file
26. EOFError pickle.load(io.BytesIO(b"")) (readline at EOF returns "")
27. (no error) (no throw/catch: a control-flow exception is just an exception)
28. NotImplementedError raise NotImplementedError
29. SyntaxError compile("def", "<string>", "exec")
Python's own reference table is Built-in Exceptions ↗; there is no Python-library page on errors yet, and the Python twins throughout this chapter print type names for the reason given above.
Try it¶
- Write
explain(code) { … }that runs its block and prints a one-line diagnosis for the three commonest classes —NoMethodError(say whether the receiver wasnil, usinge.receiver),KeyError(name the key, usinge.key) andArgumentError— andunhandled: Classfor any otherStandardError. Run it on six snippets from the table, including one that does not raise. - Pick five rows and write the fixed version of each snippet beside the broken one, printing the value the fixed version returns; then run the file under
ruby -wand check it is silent.
Solution to 1
common_error_messages_kata_rb.rb in full — pasted here by tools/run_examples.py from the file CI runs.
# Exercise 1: explain runs a block and turns the three commonest errors into a
# one-line diagnosis, leaving everything else labelled as unhandled.
def explain(code)
yield
puts " #{code.ljust(22)} ok"
rescue NoMethodError => e
hint = e.receiver.nil? ? "a nil reached a method call; guard it or use &." : "no such method; check the spelling and the receiver's class"
puts " #{code.ljust(22)} #{e.class}: #{hint}"
rescue KeyError => e
puts " #{code.ljust(22)} #{e.class}: use fetch with a default, or [] (key #{e.key.inspect})"
rescue ArgumentError => e
puts " #{code.ljust(22)} #{e.class}: check the call's arguments (#{e.message})"
rescue StandardError => e
puts " #{code.ljust(22)} unhandled: #{e.class}"
end
explain("nil.length") { nil.length }
explain('"s".zzz') { "s".zzz }
explain("{a: 1}.fetch(:x)") { { a: 1 }.fetch(:x) }
explain('Integer("abc")') { Integer("abc") }
explain("1 / 0") { 1 / 0 }
explain("[1, 2].first") { [1, 2].first }
Verified output of common_error_messages_kata_rb.rb — regenerated by tools/run_examples.py, never hand-typed.
nil.length NoMethodError: a nil reached a method call; guard it or use &.
"s".zzz NoMethodError: no such method; check the spelling and the receiver's class
{a: 1}.fetch(:x) KeyError: use fetch with a default, or [] (key :x)
Integer("abc") ArgumentError: check the call's arguments (invalid value for Integer(): "abc")
1 / 0 unhandled: ZeroDivisionError
[1, 2].first ok
See also¶
- A bare
rescuecatchesStandardError, not everything — where every class in the table sits in the tree raisehas four forms — how to raise these yourself, with the messages you choose- Custom exceptions subclass
StandardError— when none of the built-in classes says what went wrong - Safe navigation — row 1, the most common error of all
- Arguments: positional, keyword and splat — rows 6 to 8
- Numeric coercion — rows 10 and 11
NoMatchingPatternError— rows 19 and 20- Built-in Exceptions ↗ — the Python reference table
rescueworks as a modifier, forStandardErroronly —expr rescue fallbackevaluatesexprand, if it raises aStandardError, yieldsfallbackinstead; it…method_missingneedsrespond_to_missing?—method_missing(name, *args)runs only after normal lookup has failed, and it must callsuperfor names…sendreaches private methods;public_senddoes not —obj.send(:name, *args)calls a method by name and ignores visibility,public_sendhonours it, and…- Twenty-three Ruby traps for a Python programmer — The twenty-three places where a Python reflex gives the wrong answer in Ruby