Feature #4521

NoMethodError#message may take very long to execute

Added by adiel.mittmann (Adiel Mittmann) about 6 years ago. Updated over 4 years ago.

Target version:


When a non-existing method is called on an object, NoMethodError is risen. If you call #message, however, your code may use up all CPU for a very long time (in my case, up to a few minutes).

I narrowed the problem down to this code in error.c (SVN snapshot) in the function name_err_mesg_to_str():

d = rb_protect(rb_inspect, obj, 0);
if (NIL_P(d) || RSTRING_LEN(d) > 65) {
d = rb_any_to_s(obj);

The problem is that, for a complex object, #inspect may take very long to execute, only to have its results thrown away because they will be larger than 65 characters.

Of course I can write a #to_s for all my objects, but the point is that I didn't call #to_s or #inspect, I called #message on an exception object, which then takes a few minutes just to return a short string.

Needless to say, this might be easy to spot in a simple example, but once you're writing a web application that suddenly freezes for one minute with no apparent reason, you're all but clueless as to what's going on. (The first time this happened, I didn't even know that something would eventually show up on the screen -- I thought it was an infinite loop).

Here's an example code that shows this behavior:

require 'nokogiri'
class A
def x
@xml = Nokogiri::XML('baz.xml', 'rb').read())

Here, the time it takes for Ruby to print out the message that #foo doesn't exist is proportional to the size of baz.xml.

As a comparison, Python doesn't seem to do this. Take the following code:

class Test:
def str(self):
return "hello"
a = Test()
print a
print a.x()

If you execute it, this is the result:

Traceback (most recent call last):
File "", line 6, in
print a.x()
AttributeError: Test instance has no attribute 'x'

It uses the method str to convert the object to a string when necessary, but doesn't use it when printing out the message stating that the attribute doesn't exist.

One obvious way to fix this would be to always print out the simpler representation given by rb_any_to_s.


#1 Updated by ko1 (Koichi Sasada) almost 6 years ago

  • Tracker changed from Bug to Feature
  • Assignee set to matz (Yukihiro Matsumoto)

It is not a bug, but a feature request.

#2 Updated by shyouhei (Shyouhei Urabe) about 5 years ago

  • Status changed from Open to Assigned

#3 [ruby-core:49700] Updated by mame (Yusuke Endoh) over 4 years ago

  • Description updated (diff)
  • Target version set to next minor

Also available in: Atom PDF