Can't dup NilClass… maybe try `unloadable`

If you’re using a model or controller from an engine in Rails 2.3.2 you may encounter some crazy errors from time to time. Errors like: A copy of ApplicationController has been removed from the module tree but is still active! Or sometimes the even weirder one: can't dup NilClass

In one situation where you get A copy of ApplicationController has been removed from the module tree but is still active! it could be because you’re using a plugin or engine that provides controllers and inherits from ApplicationController. Some of the classes inherited or included in your engine controllers may fail to get unloaded and cause trouble after the first request to your system. Add unloadable inside your controller classes in your engine.

The can't dup NilClass error really tricked me though. It seemed to be saying something about duping nil class but not really. It was a lie! Well, almost. See, I had a model in my engine like this:

class Account  :destroy

  attr_accessible :nickname, :email

And I had a module in my app (not in the engine) like this (sort of a reverse-micro-plugin):

module Authentication
  module AccountBuddy
    def self.included(account)
      account.class_eval do
        has_many :characters
      end
    end
  end
end

And when I tried to access account.characters it was all “can’t dup NilClass”. But what it really meant to say was: “I’m returning nil instead of an array containing the characters belonging to this account because I became confused during the loading and unloading of all your crazy ass models. -Love, Rails”

So adding a little unloadable also fixes that.

class Account < ActiveRecord::Base
  unloadable #  :destroy

  attr_accessible :nickname, :email

What a load off.

A better way

Courtesy of Paul (from the comments)

unloadable is now deprecated, I think. As far as I am aware, the “right” way to do this is one of the following:

First, you could put something like this in your plugin’s init.rb file:

# This plugin should be reloaded in development mode.
if RAILS_ENV == 'development'
  ActiveSupport::Dependencies.load_once_paths.reject!{|x| x =~ /^#{Regexp.escape(File.dirname(__FILE__))}/}
end

Second, you could put something like this in your application’s environment.rb file:

config.reload_plugins = true if RAILS_ENV == ‘development’

If the above did not solve your problem:

Courtesy of Evan Owen (from the comments)

Another possible cause of this problem is that ActiveRecord also defines self.included. In order to ensure that ActiveRecord gets the call and can finish loading the model, a call to super needs to be added to the end of self.included like so:

def self.included(account)
  account.class_eval do
    has_many :characters
  end

  super # fixes the "can’t dup nil" issue
end

This did not work in my specific case, but may be the correct solution if you are suffering from a similar but different problem.

Author: Daniel X

Heretic priest of the Machine God. I enjoy crawling around in Jeff Bezo's spaceship, bringing technology to the people, and long walks outside of time and space.

38 thoughts on “Can't dup NilClass… maybe try `unloadable`”

  1. Thank you for this entry. I’ve been fighting these issues all over the place for about 4 weeks and yours is the first place I’ve found a solution to both issues I’ve been having.

    Strange: The “{}Controller has been removed” issue is by design (and has been known and not well documented since 2006 (I found the solution in trac about 3 hours before I found this post).

    Like

  2. htf did you figure this out? haha.

    you just saved my day. i have an app that is built up of all kinds of desert_plugins and shortly after updating to 2.3.2 i started getting that weird “Can’t dup NilClass error.”

    my conflict was in a model though. the first time my page would load (after a server start) everything worked fine. the second time the page loaded, i got the NilClass error.

    adding ‘unloadable’ to the model in my desert plugin solved it all.

    thanks for the tip!

    Like

  3. unloadable is now deprecated I think. As far as I am aware, the “right” way to do this is one of the following.

    First, you could put something like this in your plugin’s init.rb file:
    # This plugin should be reloaded in development mode.
    if RAILS_ENV == ‘development’
    ActiveSupport::Dependencies.load_once_paths.reject!{|x| x =~ /^#{Regexp.escape(File.dirname(__FILE__))}/}
    end

    Second, you could put something like this in your application’s environment.rb file:
    config.reload_plugins = true if RAILS_ENV == ‘development’

    Like

  4. Thank you! Thank you! Thank you!

    I’ve been beating my head against this obscure error for days, doing work on an engine in rails 2.3.2.

    Cut. Paste. Solved.

    Now, where was I?

    Like

  5. I used Paul’s solution because it evidently affects development environment only. That plus his statement that unloadable might be deprecated.. But anyway thanks to both of you daniel and paul!

    Like

  6. Thanks man, this post helped me out. I ended up putting:

    load_paths.each do |path|
    ActiveSupport::Dependencies.load_once_paths.delete(path)
    end if config.environment == ‘development’

    in my engine’s init.rb file. All good.

    Like

  7. I’m still trying to figure out a problem that produces the same error message. I’m not completely up to speed on unloadable. We’re not using any plugins or engines here, I’ve reproduced the problem in a sample rails app. The problem happens with Paperclip and multiple file uploads from a flash uploader. The first one works great, but the second POST request produces the error. If anyone could take a look at this that would be great. It is a simple example of file uploads with JS+Flash, currently only working for one upload.

    http://github.com/webandy/uploadifytest/tree/master

    Like

  8. I was getting this exact same error, in development mode only. It turned out the problem was in a file in my “lib” directory, which was calling “require” for a couple of models. This *somehow* caused the models to not be loaded properly on subsequent requests (thus it would work the first time), causing the “Can’t dup NilClass” error that has driving me absolutely insane for almost 48 hours.

    There a little bit more background on the *why*, plus other, similar scenarios in which this could happen on these two threads:

    http://groups.google.com/group/rubyonrails-talk/browse_thread/thread/f54f18f4d4354926?pli=1

    http://groups.google.com/group/rubyonrails-core/browse_thread/thread/0675477c3fb23bf2/787b561d166abf53?#787b561d166abf53

    Like

  9. This explains the error that suddenly started in my app. I had added a call to self.dosomething from a scheduled task created by Rufus.Scheduler. This must have loaded my model into the engine or some weird thing, locking up regular ‘interactive’ access to the model.

    Thank you!!!!

    Like

  10. I’m having a Can’t dup NilClass, but unloadable didn’t do the trick, pitty. Anyway, just a little typo in your post: in the example, you wrote ‘unloabable’. 🙂

    Like

  11. It doesn’t seem to be fixed in 2.3.4 and unloadable is deprecated, but thanks to this post I found the solution to add the below to init.rb.

    load_paths.each do |path|
    ActiveSupport::Dependencies.load_once_paths.delete(path)
    end if config.environment == ‘development’

    Like

  12. Thanks so much! I was getting can’t dup NilClass when adding a has_many :through on a model that is defined inside an engine. Adding this to my environment.rb solved the problem!

    config.reload_plugins = true if RAILS_ENV == ‘development’

    Like

  13. @Brad: Ta, my messages were also caused by an unnecessary “require ‘user'” in a file in the lib folder. After removing the “require ‘user'” the problem went away. Another problem that went away when I did this was before_update being called twice in test mode (http://dev.rubyonrails.org/ticket/480).

    Like

  14. Snap this started happening today after I updated my XUbuntu system to the latest. I guess the process fudged my rails files. I am not using engines. Maybe it is because I have a model named User? What gives? I don’t want to go around putting some crazy ass line like unloadable in all my models. That is just stupid in my opinion.

    Like

  15. I still get the problem, and I don’t know how to fix it. The solutions listed above cannot fix it. The only option I can choose now is to downgrade to rails 2.2.2

    Like

  16. Actually, the real cause of this problem is that ActiveRecord also defines self.included, and since you’re not calling “super” inside your method, ActiveRecord never gets the call and can’t finish loading your model, causing the “can’t dup nil” errors when you try to call the association.

    To fix this, simply do:

    def self.included(account)
    account.class_eval do
    has_many :characters
    end
    super # fixes the “can’t dup nil” issue
    end

    Like

  17. @Fred – in my case the init.rb in my plugin as that is where I was getting the error. It maybe different if you are getting the error for your Rails app itself…

    Like

  18. Just wanted to add yet another method of addressing this issue (when all the others don’t seem to work for you). Credit goes to James Adam for pointing me in the right direction and the comments in this article.

    See http://groups.google.com/group/rubyonrails-talk/browse_thread/thread/f54f18f4d4354926?pli=1 for an explanation.

    In environment.rb:

    Rails::Initializer.run do |config|
    # etc
    config.to_prepare do
    # use load to ensure the added functionality is actually evaluated every time
    load 'path/to/your/extension.rb'
    end
    end

    Like

  19. I used Kris fix in the plugin init.rb and it fixed the problem and the “A copy of ApplicationController has been removed from the module tree but is still active!” has gone.
    Thanks Kris & Daniel

    Like

  20. I just ran into the ‘A copy of ApplicationController has been removed from the module tree but is still active!’ problem using Rails 2.3.8 and BrowserCMS 3.1.2.
    As soon as I was calling ApplicationController’s methods from my custom controllers, the exception would occur.

    Paul’s solution works nicely for me.


    Paul:

    Second, you could put something like this in your application’s environment.rb file:
    config.reload_plugins = true if RAILS_ENV == ‘development’

    Like

  21. The “Can’t dup NilClass” error can also happen with Time.parse when you pass in a nil value:

    oh_noes = nil
    Time.parse(oh_noes)

    I assume the parse method dupes the value to make sure it doesn’t modify yours, hence the error.

    This is what caused ours. 🙂 Likewise I assume any method that tries to dup without checking for nil first will throw this.

    Like

  22. Solution similar to Nathan Hyde’s above:

    Rails 2.3.5

    Problem: Dev mode wants to reload before every request. Rails initialization gets messed up when reloading code from plugins and monkeypatches. The plugin or patch isn’t loaded at all or, if it is, things that you expect to be defined at reload time aren’t.

    My case: monkeypatching the Tag model in acts_as_taggable_on_steroids to add a has_many association

    Solution: Wrap the monkeypatch code in:

    require ‘dispatcher’
    Dispatcher.to_prepare do
    Tag.class_eval do
    # patches
    end
    end

    Resources:
    http://stackoverflow.com/questions/2397740/how-to-extend-an-unloadable-rails-plugin
    http://www.oliyiptong.com/blog/2008/08/22/rails-to_prepare-executing-code-before-each-request/

    Just another option in case you don’t want to edit environment.rb.

    Like

  23. The comment by Evan saved my day!

    I had the same error when using self.inherited. ActiveRecord::Base also defines this method, not only included – calling super solved the problem with “can’t dup NilClass”. Great post, thanks.

    Like

Leave a comment