Bug #9931

irb: Really weird behavior for x = "#{x\"}" (ex: irb(main:009:-4))

Added by jesserayadkins (Jesse Adkins) over 6 years ago. Updated about 4 hours ago.

Target version:
ruby -v:
ruby 1.9.3p484 (2013-11-22 revision 43786) [x86_64-linux]


Our journey starts off rather simple:

irb(main):001:0> x = "#{x}"
=> ""

Okay, makes sense.

irb(main):002:0> x = "#{x\"}"

Hmm...Still in a string? That's weird.

irb(main):003:0" "

Things seem back to normal but...

irb(main):004:0> x

Nothing? But x is defined!


Nothing? Hmm...

irb(main):005:0> end
irb(main):006:-1> end

Uhh...I don't think that's supposed to do that.

irb(main):007:-2> end
irb(main):008:-3> end

...I'm afraid to go deeper.

Get me out of here!

irb(main):009:-4> quit


irb(main):010:-4> C

...Can I go now?

irb(main):010:0> quit


Additionally, if I put this in a file:

x = "#{x\"}"

I get two syntax errors (ruby 1.9.3):

test.rb:1: syntax error, unexpected $undefined, expecting '}'
x = "#{x\"}"
test.rb:1: syntax error, unexpected $end, expecting '}'
x = "#{x\"}"

Updated by jaredbeck (Jared Beck) over 6 years ago

Interesting. Let's try to figure it out!

The irb prompt

The prompt has two numbers. The first is obviously a line number, but what is the second number?

irb(main):001:0> "#{
irb(main):002:0> end
irb(main):003:-1> end
irb(main):004:-2> do
irb(main):005:-1> do
irb(main):006:0> }"
SyntaxError: (irb):2: syntax error, unexpected keyword_end
(irb):6: unterminated string meets end of file
  from /Users/jared/.rbenv/versions/2.1.2/bin/irb:11:in `<main>'

The second number seems to be a simple estimate of block depth. It must be implemented very simply, if it is capable of being negative.

String interpolation is a block

irb(main):013:0> "#{
irb(main):014:0>   [1, 2, 3].map { |x| x * 2 }
irb(main):015:0" }"
=> "[2, 4, 6]"

It's just a block! We can put whatever we want in there. :)

Note that on the third line, the prompt character changed from > to ", indicating (roughly) that irb thinks we're inside a string.

The syntax error

In the given example


the string is closed at col. 5. The contents of the string are:


We can see that the interpolation block was not closed. So, the syntax error

expecting '}'

makes sense.

Updated by jaredbeck (Jared Beck) over 6 years ago

PS: Check out the pitchfork book's (2009 edition) chapter on strings, especially the part about interpolation on p. 110.

Updated by jeremyevans0 (Jeremy Evans) over 1 year ago

  • Assignee set to keiju (Keiju Ishitsuka)
  • Status changed from Open to Assigned

This appears to still be a problem in the master branch, though the details have changed slightly. You still need Ctrl-C to reset the state after the x = "#{x\"}" line:

irb(main):001:0> x = "#{x\"}"
irb(main):001:0> ;
irb(main):001:0> 1
irb(main):001:0> nil
irb(main):001:0> # Ctrl-D at this point has no effect
irb(main):001:0> # Ctrl-D after Ctrl-C exits

In nested irb, you can end up with deadlock due to this:

irb(main):001:0> irb
irb#1(main):001:0> x = "#{x\"}"
irb#1(main):001:0> exit
Traceback (most recent call last):
       16: from /home/jeremy/tmp/irb/lib/irb/ruby-lex.rb:84:in `block in each_top_level_statement'
       15: from /home/jeremy/tmp/irb/lib/irb/ruby-lex.rb:84:in `loop'
       14: from /home/jeremy/tmp/irb/lib/irb/ruby-lex.rb:99:in `block (2 levels) in each_top_level_statement'
       13: from /home/jeremy/tmp/irb/lib/irb.rb:493:in `block in eval_input'
       12: from /home/jeremy/tmp/irb/lib/irb.rb:650:in `signal_status'
       11: from /home/jeremy/tmp/irb/lib/irb.rb:496:in `block (2 levels) in eval_input'
       10: from /home/jeremy/tmp/irb/lib/irb/context.rb:404:in `evaluate'
        9: from /home/jeremy/tmp/irb/lib/irb/workspace.rb:85:in `evaluate'
        8: from /home/jeremy/tmp/irb/lib/irb/workspace.rb:85:in `eval'
        7: from (irb):1:in `irb_binding'
        6: from /home/jeremy/tmp/irb/lib/irb/extend-command.rb:154:in `irb'
        5: from /home/jeremy/tmp/irb/lib/irb/extend-command.rb:151:in `irb'
        4: from /home/jeremy/tmp/irb/lib/irb/cmd/nop.rb:20:in `execute'
        3: from /home/jeremy/tmp/irb/lib/irb/cmd/subirb.rb:20:in `execute'
        2: from /home/jeremy/tmp/irb/lib/irb/ext/multi-irb.rb:229:in `irb'
        1: from /home/jeremy/tmp/irb/lib/irb/ext/multi-irb.rb:229:in `stop'
fatal (No live threads left. Deadlock?)
1 threads, 1 sleeps current:0x000004f3ae7d1e00 main thread:0x000004f3ae7d1e00
* #<Thread:0x000004f39ef17f70 sleep_forever>
   rb_thread_t:0x000004f3ae7d1e00 native:0x000004f3d8a08000 int:0
   /home/jeremy/tmp/irb/lib/irb/ext/multi-irb.rb:229:in `stop'
   /home/jeremy/tmp/irb/lib/irb/ext/multi-irb.rb:229:in `irb'
   /home/jeremy/tmp/irb/lib/irb/cmd/subirb.rb:20:in `execute'
   /home/jeremy/tmp/irb/lib/irb/cmd/nop.rb:20:in `execute'
   /home/jeremy/tmp/irb/lib/irb/extend-command.rb:151:in `irb'
   /home/jeremy/tmp/irb/lib/irb/extend-command.rb:154:in `irb'
   (irb):1:in `irb_binding'
   /home/jeremy/tmp/irb/lib/irb/workspace.rb:85:in `eval'
   /home/jeremy/tmp/irb/lib/irb/workspace.rb:85:in `evaluate'
   /home/jeremy/tmp/irb/lib/irb/context.rb:404:in `evaluate'
   /home/jeremy/tmp/irb/lib/irb.rb:496:in `block (2 levels) in eval_input'
   /home/jeremy/tmp/irb/lib/irb.rb:650:in `signal_status'
   /home/jeremy/tmp/irb/lib/irb.rb:493:in `block in eval_input'
   /home/jeremy/tmp/irb/lib/irb/ruby-lex.rb:99:in `block (2 levels) in each_top_level_statement'
   /home/jeremy/tmp/irb/lib/irb/ruby-lex.rb:84:in `loop'
   /home/jeremy/tmp/irb/lib/irb/ruby-lex.rb:84:in `block in each_top_level_statement'
   /home/jeremy/tmp/irb/lib/irb/ruby-lex.rb:83:in `catch'
   /home/jeremy/tmp/irb/lib/irb/ruby-lex.rb:83:in `each_top_level_statement'
   /home/jeremy/tmp/irb/lib/irb.rb:492:in `eval_input'
   /home/jeremy/tmp/irb/lib/irb.rb:431:in `block in run'
   /home/jeremy/tmp/irb/lib/irb.rb:430:in `catch'
   /home/jeremy/tmp/irb/lib/irb.rb:430:in `run'
   /home/jeremy/tmp/irb/lib/irb.rb:388:in `start'
   /home/jeremy/tmp/irb/exe/irb:11:in `<main>'
Maybe IRB bug!

Updated by hsbt (Hiroshi SHIBATA) about 1 year ago

  • Assignee changed from keiju (Keiju Ishitsuka) to aycabta (aycabta .)

Updated by jeremyevans0 (Jeremy Evans) about 4 hours ago

  • Status changed from Assigned to Closed

As of Ruby 3.0, I think this is fixed:

$ irb30
irb(main):001:0" x = "#{x\"}"
irb(main):003:0" }
irb(main):004:0" "
Traceback (most recent call last):
        3: from /usr/local/bin/irb30:23:in `<main>'
        2: from /usr/local/bin/irb30:23:in `load'
        1: from /usr/local/lib/ruby/gems/3.0/gems/irb-1.3.0/exe/irb:11:in `<top (required)>'
SyntaxError ((irb):1: syntax error, unexpected backslash)
x = "#{x\"}"

This behavior makes sense. It's invalid syntax, so it is reasonable that irb would expect that after "#{x\"}", you are still in an interpolation inside a string, and as soon as it knows the interpolation has ended, it evaluates the string and you get a syntax error.

Also available in: Atom PDF