Rails: interning empty string

This is one of those errors in Rails that just make you go WTF!? The amount of debug information is minimal, and the row that supposedly is the culprit is syntactically correct. Even Google doesn’t really help. I gather that this error can occur in several different contexts and caused by different things, so I’m not trying to provide an all purpose solution to this. The case I’m going to show you is probably quite rare, but it happened to us at work today. I never figured out exactly why Rails chokes in this situation, but I found a way to reproduce the problem and also a solution. The main characters in this drama are the respond_to and render methods, and the main villain is a mysterious invisible parameter.

To reproduce, follow these steps:

$> rails -dmysql testapp
$> cd testapp
$> ./script/generate scaffold User name:string email:string
$> emacs db/migrate/*_create_users.rb

Add the following line after the create_table block in the self.up method.

User.create(:name => 'Fredrik', :email => 'fredrik@fredrikbostrom.net')

Then create the database.

$> rake db:create
$> rake db:migrate

Now you’re ready to start your server.

$> ./script/server -p 3000

Everything should work fine when you navigate to http://localhost:3000/users/1. Now let’s add some more stuff. Create a partial named _produce_error.html.erb in the app/views/users/ directory. Put some html in it.

$> echo "<h1>It works.</h1>" > app/views/users/_produce_error.html.erb

Next, edit the show method in your app/controllers/users_controller.rb and add the following line just before the respond_to block:

render :partial => "produce_error" and return

Save. Now your show method should look like this:

  # GET /users/1
  # GET /users/1.xml
  def show
    @user = User.find(params[:id])

    render :partial => "produce_error" and return

    respond_to do |format|
      format.html # show.html.erb
      format.xml  { render :xml => @user }
    end
  end

Next, open your browser and navigate to http://localhost:3000/users/1. This should still work and display “It works.” in big bold letters. So far so good. We bypassed the respond_to block with our “render and return”, just as expected. But what happens if we add some parameters to the url, like this:

http://localhost:3000/users/1?format=

Violà! The villain reveals himself. Rails throws an ArgumentError with the explanation “interning empty string”, and the audience goes “wth just happened”? The beginning of the error log looks like

Processing UsersController#show to  (for 127.0.0.1 at 2010-03-11 22:22:07) [GET]
  Parameters: {"id"=>"1"}
  User Columns (1.8ms)   SHOW FIELDS FROM `users`
  User Load (0.2ms)   SELECT * FROM `users` WHERE (`users`.`id` = 1)

  ArgumentError (interning empty string):
  app/controllers/users_controller.rb:19:in `show'

  Processing ApplicationController#show to  (for 127.0.0.1 at 2010-03-11 22:22:08) [GET]
  Parameters: {"id"=>"1"}

  ArgumentError (interning empty string):

  /! FAILSAFE /!  Thu Mar 11 22:22:08 +0200 2010
  Status: 500 Internal Server Error
  interning empty string
    /usr/local/lib/ruby/gems/1.8/gems/actionpack-2.3.3/lib/action_view/base.rb:282:in `to_sym'

Now, in this case it’s quite easy to se what’s going on. We added an empty format parameter and Rails doesn’t want to play anymore. Normally, the format parameter is inferred from the suffix of the id paramter, i.e.

http://localhost:3000/users/1.xml

which would trigger the xml format in render_to. However, Rails also understands the format parameter given among the other GET parameters in the URL, although it doesn’t show in the “Parameters” list in the log output. It’s rather obvious, at least in hindsight, that Rails still tries to use the format parameter, and since it’s empty (not nil, but an empty string), it chokes on it. The only place you could get a hint of this is in the line

Processing UsersController#show to  (for 127.0.0.1 at 2010-03-11 22:22:07) [GET]

of the log, where the “to” word indicates that Rails is trying to reformat the result into something, but that something is empty, which just doesn’t compute!

What I don’t quite understand is why Rails insists on using the empty parameter in the respond_to block in the first place, when we explicitly exit the method after the render statement, nor why it doesn’t check whether the format parameter is empty or not before trying to use it. Bug, perhaps?

Now, when we’ve identified the problem, the workaround is simple. We just have to make sure the format parameter is either nil or some non-empty string. If we can’t (or can’t be bothered to) change the parameter in the URL, we can do so in the controller before the render statement:

  # GET /users/1
  # GET /users/1.xml
  def show
    @user = User.find(params[:id])

    params[:format] = nil if params[:format] and params[:format].empty?
    render :partial => "produce_error" and return

    respond_to do |format|
      format.html # show.html.erb
      format.xml  { render :xml => @user }
    end
  end

This makes sure Rails will ignore the format all together and will render the partial and exit the method as a happy little squirrel.

Ruby 1.8.6, Rails 2.3.3, Mongrel. Thanks to Neeraj for enlightening me with the anatomy of the respond_to method.

Advertisements

6 Comments

Filed under Geek speak, Ruby on Rails

6 responses to “Rails: interning empty string

  1. Tom

    Thanks for this article! This one was confusing me… and I’m still not sure why “format” is set to empty string.

    Quick correction though in the last code block you have this line:
    params[:format] = nil if params[:format] and params[:foramt].empty?

    And the “format” symbol is misspelled “foramt”.

    Thanks again for the great article!

  2. Tom, the format parameter’s empty value comes from http://localhost:3000/users/1?format= where the format parameter is present but doesn’t have a value.

    If the format parameter would be missing from the url, however, the value of params[:format] would be nil.

    And thanks for the spellcheck 😉

  3. This is great info, thanks for the write up Fredrik, very well done. I have this error thrown occasionally in my app too. Had no idea why this was happening until I found your article. Cheers!

  4. Nico

    WonderfuL! Was almost going mad over this one. In my case it is produced by a googlebot that appends the ?format= to soem of our apps urls. I still have no clue why it does it…

    Nico

  5. I was getting this error yesterday afternoon. My context was a bit different, but I found that whenever I called the to_sym on an empty string, I got the “interning empty string” error.

  6. I’m getting the same error right now when trying to use the gem formtastic version 0.2.4. My stackoverflow question can be found here http://stackoverflow.com/questions/8453521/ror-v2-3-error-actionviewtemplateerror-interning-empty-string I was wondering if you had any ideas on what the problem is since I have never seen an error called interning empty string.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s