Carrierwave Failing Silently

Overview

CarrierWave is my favourite file upload gem, recently I ran into an issue whereby it’s upload from url feature was failing silently. Here’s how I resolved the issue using pry and pry-byebug

This post may be useful if you are a beginner who wants to learn about debugging techniques, or you just ran into the same issue with CarrierWave.

CarrierWave Remote Uploads Not Working

As you can see, uploading via url is not working; company.logo is nil.

`rails console`
1
2
3
4
5
6
7
8
9
10
11
12
13
[11] pry(main)> Company.last.remote_logo_url = 'https://media.licdn.com/mpr/mpr/p/8/005/0a1/15e/0b54975.png'
  Company Load (0.9ms)  SELECT  "companies".* FROM "companies"   ORDER BY "companies"."id" DESC LIMIT 1
=> "https://media.licdn.com/mpr/mpr/p/8/005/0a1/15e/0b54975.png"
[12] pry(main)> Company.last.save
  Company Load (0.6ms)  SELECT  "companies".* FROM "companies"   ORDER BY "companies"."id" DESC LIMIT 1
   (0.2ms)  BEGIN
   (0.3ms)  COMMIT
=> true
[13] pry(main)> Company.last.logo
  Company Load (0.6ms)  SELECT  "companies".* FROM "companies"   ORDER BY "companies"."id" DESC LIMIT 1
=> #<CompanyLogoUploader:0x007fe458100060
 @model=#<Company id: 11, linkedin_id: nil, name: nil, freelancer_position_id: 21, description: nil, website: nil, logo: nil>,
 @mounted_as=:logo>

Investigate with Pry + ByeBug

Add pry-byebug to your Gemfile and clone a copy of carrierwave to your machine, referencing it into your app using bundler’s :path option

Gemfile
1
2
3
4
5
6
group :development do
  gem 'pry-rails' # use pry when running `rails console`
  gem 'pry-byebug'
end

gem 'carrierwave', path: '../carrierwave'

The next thing I did was take a look at the CarrierWave source and made an educated guess at where the problem might be occurring. I added binding.pry to invoke Pry; (a Pry shell will fire up inside your Rails server log)

`rails server`
1
2
3
4
5
6
7
8
9
From: /Users/benwoodward/dev/carrierwave/lib/carrierwave/uploader/download.rb @ line 73 CarrierWave::Uploader::Download#download!:

    68: def download!(uri)
    69:   processed_uri = process_uri(uri)
    70:   file = RemoteFile.new(processed_uri)
    71:   raise CarrierWave::DownloadError, "trying to download a file which is not served over HTTP" unless file.http?
    72:   binding.pry
 => 73:   cache!(file)
    74: end

The root of the problem is revealed

Using the next command provided by pry-byebug, I walked my way through the call stack until I discovered the root of the problem. (Walking through the stack like this is a great way to learn about how a Gem works in my opinion).

`rails server`
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
[2] pry(#<CompanyLogoUploader>)> next
Next went up a frame because previous frame finished

From: /Users/benwoodward/dev/carrierwave/lib/carrierwave/mounter.rb @ line 88 CarrierWave::Mounter#remote_urls=:

    71: def remote_urls=(urls)
    72:   return if not urls or urls == "" or urls.all?(&:blank?)
    73:
    74:   @remote_urls = urls
    75:   @download_error = nil
    76:   @integrity_error = nil
    77:
    78:   @uploaders = urls.map do |url|
    79:     uploader = blank_uploader
    80:     uploader.download!(url)
    81:     uploader
    82:   end
    83:
    84: rescue CarrierWave::DownloadError => e
    85:   @download_error = e
    86:   raise e unless option(:ignore_download_errors)
    87: rescue CarrierWave::ProcessingError => e
 => 88:   @processing_error = e
    89:   raise e unless option(:ignore_processing_errors)
    90: rescue CarrierWave::IntegrityError => e
    91:   @integrity_error = e
    92:   raise e unless option(:ignore_integrity_errors)
    93: end

[2] pry(#<CarrierWave::Mounter>)> e
=> #<CarrierWave::ProcessingError: Failed to manipulate with rmagick, maybe it is not an image?>
[3] pry(#<CarrierWave::Mounter>)> option(:ignore_processing_errors)
=> true

Oh dear. Turns out CarrierWave has some configuration options that I didn’t see mentioned in the documentation. Perhaps I should have researched the configuration options first, oh well. Next step, switch the errors back on in development…

config/environments/development.rb
1
2
3
4
5
CarrierWave.configure do |config|
  config.ignore_integrity_errors = false
  config.ignore_processing_errors = false
  config.ignore_download_errors = false
end

..which gives me useful errors in the console.

Anyway, now I know what the error tells me is there is a problem with RMagick or Imagemagick (unsurprisingly!). Turns out my Imagemagick installation is missing a png decode delegate.

Callbacks

`rails console`
1
2
3
4
5
6
7
8
9
[13] pry(main)> Company.last.remote_logo_url = 'https://media.licdn.com/mpr/mpr/p/8/005/0a1/15e/0b54975.png'
  Company Load (0.7ms)  SELECT  "companies".* FROM "companies"   ORDER BY "companies"."id" DESC LIMIT 1
=> "https://media.licdn.com/mpr/mpr/p/8/005/0a1/15e/0b54975.png"
[14] pry(main)> Company.last.store_logo!
  Company Load (0.7ms)  SELECT  "companies".* FROM "companies"   ORDER BY "companies"."id" DESC LIMIT 1
=> []
[15] pry(main)> Company.last
  Company Load (0.6ms)  SELECT  "companies".* FROM "companies"   ORDER BY "companies"."id" DESC LIMIT 1
=> #<Company id: 11, linkedin_id: nil, name: nil, freelancer_position_id: 21, description: nil, website: nil, logo: nil>

CarrierWave includes callbacks into your ActiveRecord model instance. . The after_save :"store_#{column}!" callback triggers the store! method on the cached file, a reference to which is only stored in memory while your model is instantialised. So we need to keep the instance in memory so before calling save on it in order to save the cached file. A useful trick I’ve used here is to retrieve the last return value with underscore

`rails console`
1
2
3
4
5
6
7
8
9
10
11
12
comp = _
  Company Load (0.6ms)  SELECT  "companies".* FROM "companies"   ORDER BY "companies"."id" DESC LIMIT 1
=> #<Company id: 11, linkedin_id: nil, name: nil, freelancer_position_id: 21, description: nil, website: nil, logo: nil>
[18] pry(main)> comp.remote_logo_url = 'https://media.licdn.com/mpr/mpr/p/8/005/0a1/15e/0b54975.png'
=> "https://media.licdn.com/mpr/mpr/p/8/005/0a1/15e/0b54975.png"
[19] pry(main)> comp.save
   (1.0ms)  BEGIN
  SQL (1.9ms)  UPDATE "companies" SET "logo" = $1 WHERE "companies"."id" = 11  [["logo", "0b54975.png"]]
   (3.5ms)  COMMIT
=> true
[20] pry(main)> comp
=> #<Company id: 11, linkedin_id: nil, name: nil, freelancer_position_id: 21, description: nil, website: nil, logo: "0b54975.png">

Problem solved!

Conclusion

Pry is an extremely powerful tool for debugging Ruby apps and also learning about how codebases work and always my go-to tool for these kind of scenarios. If you found this useful, have any suggestions about how I might have solved this problem efficiently, or even Pry tips; please let me know in the comments.

Comments