Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ActiveJob::SerializationError: Unsupported argument type: StringIO #319

Open
the-teacher opened this issue Jan 15, 2016 · 14 comments
Open

Comments

@the-teacher
Copy link

the-teacher commented Jan 15, 2016

Hello!
I am looking for way how to put sending notifications to the background.

I tried play with exception_notification (4.1.4). I found option for email notifications deliver_with but it looks like allow the just deliver_now value.

Fine, I wrote following monkey patch for my test app:

config/initializers/exception_notifier_patch.rb

module ExceptionNotifier
  class EmailNotifier < BaseNotifier
    def call(exception, options={})
      binding.pry
      # create_email(exception, options).send(deliver_with)
      create_email(exception, options).deliver_later
    end
  end
end

And I have an error message for Sidekiq:

ActiveJob::SerializationError: Unsupported argument type: StringIO

[16] pry(#<ExceptionNotifier::EmailNotifier>)> exception.message
=> "divided by 0"

[17] pry(#<ExceptionNotifier::EmailNotifier>)> exception.class
=> ZeroDivisionError

So, for me it looks like create_email do something incorrect for delayed processors. Maybe DelayedJob can process this, but Sidekiq can't.

I want to ask, is it possible to put exception_notification mailers to sidekiq queue? Or maybe it's impossible for now, and I should stop my attempts?

Thanks for any advice!

@juanazam
Copy link

@the-teacher I was able to reproduce the issue.

As you mentioned above, there is no easy way of using deliver_later as exception_notification's code stands right now, but that should be easy to fix.

The real problem is the way the email is created and how ActiveJob serializes the data to pass it to sidekiq.

When the environment section for the notification email is generated, it includes this:
* rack.input : #<StringIO:0x007fca9d87a6f8>

which seems to be the culprit.

Anyway I will investigate more and try to find a workaround to send emails in the background since it seems to be a good feature to support.

@Axxiss
Copy link

Axxiss commented Feb 25, 2016

When serializing, ActiveJob only support certain type of objects, according to the SerializationError docs:

Raised when an unsupported argument type is set as a job argument. We currently support NilClass, Fixnum, Float, String, TrueClass, FalseClass, Bignum, BigDecimal, and objects that can be represented as GlobalIDs (ex: Active Record). Raised if you set the key for a Hash something else than a string or a symbol. Also raised when trying to serialize an object which can't be identified with a Global ID - such as an unpersisted Active Record model.

Furthermore rack.input is the first of many "unserializable" objects,

I thinks this leaves a few questions open.

  • Are those unserializable objects required by create_email?
  • Which of those required objects (if any) can be serialized by us?
  • Is there a way for EmailNotifier to get those objects without serialization?
  • Could be feasible to do some work (to avoid serialization of unsupported objects) on the current thread and delay what can be done on the background after serialization?

@aruprakshit
Copy link

@Axxiss Did you find answers of your question? :) I have similar questions and got stuck.

@Axxiss
Copy link

Axxiss commented Sep 6, 2016

@aruprakshit No, I didn't had a chance to look further into it.

@fmluizao
Copy link

This should be a great addition. I was wondering if it would be possible to serialize the data used in the views. I'm not familiar with the source code, but if anyone point me a direction I can give a shoot.

@xHire
Copy link

xHire commented Dec 19, 2016

I ran into this exact issue in my application. It turned out the cause is ActiveJob saving parameters to later call the original method. My solution is to use Marshal.dump on all parameters so that ActiveJob is given only a string (plus I put all parameters into an array to make it simpler). To demonstrate it on the code from the first post (untested, but should work):

module ExceptionNotifier
  class EmailNotifier
    def call(exception, options={})
      create_email(Marshal.dump([ exception, options ])).deliver_later
    end

    def create_email(params)
      super(*Marshal.load(params)))
    end
  end
end

Nice thing is that the (un)marshalling can be there even for non-delayed delivery. I hope this helps someone. :c)

@aruprakshit
Copy link

@xHire I did that way you did. But while loading sometime I was getting encoding error.

@xHire
Copy link

xHire commented Dec 19, 2016

@aruprakshit That’s interesting. Could you be more specific? Is it a normal Ruby encoding error or something special? For which encodings/contents does it happen?

Personally, I didn’t run in any such issue, but that might be because my application only works with ascii-8bit data.

@r4mbo7
Copy link

r4mbo7 commented Jun 27, 2018

2018, still looking for a way to have notifications perfomed by sikekiq...

Trying all solutions above, don't find something working yet.

It's gonna end with a custom message base on excpetion and options variables for me :/

@kadru
Copy link

kadru commented Sep 25, 2020

Hi, I just got stuck with this. I tried many things, so far this is the best approach I have:

  module EmailNotifier
    def call(exception, options = {})
      options[:env] = options[:env].transform_values do |value|  # this could be in a pre_callback?
        break if ActiveJob::Serializers.serializers.include? value.class
        value.to_s
      end

      super(exception, options)
    end
end

The problem with this approach, is have to write the serializers (see) for the classes that will used later for construct the body of email, (e.g. controller classes) and then add to active job serializers array. And only will work for Rails > 6.

Another approach its serialize to hashes the exception and options just before call deliver_later (maybe the email will have less info about the error).

What do you think?

@up_the_irons
Copy link

This also hit me in a new Rails 6.1 app. Being able to deliver exception notifications in the background is important, and we use Sidekiq to back ActiveJob. I haven't found a good solution yet.

@herunan
Copy link

herunan commented Apr 7, 2021

Would love to get a fix for this @juanazam! Thanks

@bayburin
Copy link

+1
I have the same problem. When I use sidekiq I see An error occurred when sending a notification using 'email' notifier.ActiveJob::SerializationError: Unsupported argument type: IO.

@followerstracker
Copy link

A slightly modified @kadru's version that worked for us on rails 6.1

config/initializers/exception_notifier.rb

module ExceptionNotifier

  # ...

  class EmailNotifier < BaseNotifier

    def call_with_patch(exception, options = {})
      options[:env] = options[:env].transform_values do |value|
        break if ActiveJob::Serializers.serializers.include? value.class
        value.to_s
      end

      call_without_patch(exception, options)
    end

    alias_method(:call_without_patch, :call)
    alias_method(:call, :call_with_patch)

   # ...
  end
end

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests