exception_notification icon indicating copy to clipboard operation
exception_notification copied to clipboard

ActiveJob::SerializationError: Unsupported argument type: StringIO

Open the-teacher opened this issue 9 years ago • 14 comments

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!

the-teacher avatar Jan 15 '16 12:01 the-teacher

@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.

juanazam avatar Jan 28 '16 14:01 juanazam

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?

Axxiss avatar Feb 25 '16 14:02 Axxiss

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

aruprakshit avatar Sep 06 '16 09:09 aruprakshit

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

Axxiss avatar Sep 06 '16 10:09 Axxiss

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.

fmluizao avatar Sep 28 '16 12:09 fmluizao

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)

xHire avatar Dec 19 '16 09:12 xHire

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

aruprakshit avatar Dec 19 '16 11:12 aruprakshit

@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.

xHire avatar Dec 19 '16 12:12 xHire

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 :/

r4mbo7 avatar Jun 27 '18 23:06 r4mbo7

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?

kadru avatar Sep 25 '20 17:09 kadru

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.

up_the_irons avatar Mar 26 '21 11:03 up_the_irons

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

herunan avatar Apr 07 '21 21:04 herunan

+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.

bayburin avatar Apr 12 '21 09:04 bayburin

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

followerstracker avatar Jun 30 '21 13:06 followerstracker