raven-python icon indicating copy to clipboard operation
raven-python copied to clipboard

'NoneType' object has no attribute '__module__'

Open mikebz opened this issue 8 years ago • 8 comments

We are getting a Sentry issue reported by Sentry. The issue happens in Celery executed code.

MESSAGE

Signal handler <bound method SentryCeleryHandler.process_failure_signal of <raven.contrib.celery.SentryCeleryHandler object at 0x7f7ba702e450>> raised: AttributeError("'NoneType' object has no attribute '__module__'",)

Exception: AttributeError 'NoneType' object has no attribute '__module__'

The offending code looks to be this: def should_capture(self, exc_info): exc_type = exc_info[0] exc_name = '%s.%s' % (exc_type.__module__, exc_type.__name__) exclusions = self.ignore_exceptions

Python: 2.7.11 raven-python: 5.32.0 celery: 4.0.2

mikebz avatar Jan 06 '17 20:01 mikebz

Same issue.

messense avatar Jan 17 '17 10:01 messense

We are experiencing this issue. The bug appears to be within the Celery contrib file and/or in code that patches captureException().

Looking at our Sentry traceback, it is curious that process_failure_signal() does not pass an exc_info kwarg to captureException(), but that kwarg indeed shows up in captureException() as [None, None, None]. I imagine this method is being modified to inject exc_info in some way, but it's hard to debug further without knowing more about how Sentry patches these various handlers.

P.S. Looks similar to https://github.com/getsentry/raven-python/issues/936.

sean-adler avatar Jan 24 '17 02:01 sean-adler

A possible reason for this is that you might have multiple (custom) celery applications instantiated. This installs multiple SentryCeleryHandler's and therefore multiple receivers are connected to the task_failure signal.

The first time the receiver is called everything worked fine, but in the second call, it failed. captureException() doesn't pass the exc_info and rather calls sys.exc_info() which returns None after the first call. Most probably because the exception happened in a different thread related to different celery application.

My solution was to make sure I only have a single Celery instance that configures Sentry i.e. this is instantiated only once.

class SentryCelery(celery.Celery):
    """Celery subclass that logs exceptions to Sentry"""

    def on_configure(self):
        """ Configure sentry client. """
        client = raven.Client(tags={'worker': self.main}, **settings.SENTRY_CONFIG)

        # register a custom filter to filter out duplicate logs
        register_logger_signal(client)

        # hook into the Celery error handler
        register_signal(client)

You could place a breakpoint and see how many times process_failure_signal is called for a single exception to falsify this reason. An easier approach would be placing the breakpoint on init method of your custom celery class.

nourspace avatar May 04 '17 12:05 nourspace

We run into this as well.

Does anyone mind writing a couple of lines to explain what the workaround could be in Django? I am not completely sure on how to get around this, really.

fradeve avatar Feb 07 '18 15:02 fradeve

This seems to be an old issue but i am running into the same issue.

  File "/usr/local/lib/python2.7/dist-packages/raven/base.py", line 814, in captureException
    'raven.events.Exception', exc_info=exc_info, **kwargs)
  File "/usr/local/lib/python2.7/dist-packages/raven/base.py", line 625, in capture
    elif not self.should_capture(exc_info):
  File "/usr/local/lib/python2.7/dist-packages/raven/base.py", line 818, in should_capture
    exc_name = '%s.%s' % (exc_type.__module__, exc_type.__name__)
AttributeError: 'NoneType' object has no attribute '__module__'

This seems to happen when you try to capture an exception but there has not been an exception. e.g.

sys.exc_info() == (None, None, None)

And since the tuple (None, None, None) != None the code continues i think the rule 622 should be:

if exc_info is not None and exc_info is not (None, None, None):

I will make a PR for this when i have the time

TCastelein avatar Feb 08 '18 13:02 TCastelein

I had the same issue and solved it by replacing exc_info is not None with exc_info is not None and sorted(exc_info) != sorted((None, None, None))

neerajh454 avatar Apr 25 '18 18:04 neerajh454

I only got this problem when I tried to use client.captureException outside of a try/except block.

blairg23 avatar Jun 19 '18 03:06 blairg23

Sort of workaround for using captureException outside try/except block:

import sys

import raven


def get_exc_info():
    exc_info = sys.exc_info()
    if exc_info[0] is None:
        return None
    return exc_info


def capture_exception(error)
    sentry = raven.Client(...)
    exc_info = get_exc_info()
    if exc_info:
        sentry.captureException(exc_info)
    else:
        sentry.captureMessage(error)

sorrat avatar Jul 04 '18 21:07 sorrat