Search code examples
pythonpython-3.xemailimapimaplib

AttributeError: "NoneType" object has no attribute 'decode'


I am trying to read an email from my gmail inbox in Python3. So I followed this tutorial : https://www.thepythoncode.com/article/reading-emails-in-python

My code is the following :

 username = "*****@gmail.com"
password = "******"
# create an IMAP4 class with SSL 
imap = imaplib.IMAP4_SSL("imap.gmail.com")
# authenticate
imap.login(username, password)
status, messages = imap.select("INBOX")
# total number of emails
messages = int(messages[0])
    

for i in range(messages, 0, -1):
    # fetch the email message by ID
    res, msg = imap.fetch(str(i), "(RFC822)")
    for response in msg:
        if isinstance(response, tuple):
            # parse a bytes email into a message object
            msg = email.message_from_bytes(response[1])
            # decode the email subject
            subject = decode_header(msg["Subject"])[0][0]
            if isinstance(subject, bytes):
                # if it's a bytes, decode to str
                subject = subject.decode()
            # email sender
            from_ = msg.get("From")
            # if the email message is multipart
            if msg.is_multipart():
                # iterate over email parts
                for part in msg.walk():
                    # extract content type of email
                    content_type = part.get_content_type()
                    content_disposition = str(part.get("Content-Disposition"))

                    # get the email body
                    body = part.get_payload(decode=True).decode()
                    print(str(body))

    imap.close()
    imap.logout()
    print('DONE READING EMAIL')

The libraries I am using is :

import imaplib
import email
from email.header import decode_header

However, when I execute it I get the following error message, which I don't understand because I never have an empty email in my inbox ...

Traceback (most recent call last):

  File "<ipython-input-19-69bcfd2188c6>", line 38, in <module>
    body = part.get_payload(decode=True).decode()

AttributeError: 'NoneType' object has no attribute 'decode'

Anyone has an idea what my problem could be ?


Solution

  • From the documentation:

    Optional decode is a flag indicating whether the payload should be decoded or not, according to the Content-Transfer-Encoding header. When True and the message is not a multipart, the payload will be decoded if this header’s value is quoted-printable or base64. If some other encoding is used, or Content-Transfer-Encoding header is missing, or if the payload has bogus base64 data, the payload is returned as-is (undecoded). If the message is a multipart and the decode flag is True, then None is returned. The default for decode is False.

    (Note: this link is for python2 - for whatever reason the corresponding page for python3 doesn't seem to mention get_payload.)

    So it sounds like either some part of some message:

    • is missing the content-transfer-encoding (gives email.message no indication how it is meant to be decoded), or
    • is using an encoding other than QP or base64 (email.message does not support decoding it), or
    • claims to be base-64 encoded but contains a wrongly encoded string that cannot be decoded

    The best thing to do is probably just to skip over it.

    Replace:

                        body = part.get_payload(decode=True).decode()
    

    with:

                        payload = part.get_payload(decode=True)
                        if payload is None:
                            continue
                        body = payload.decode()
    

    Although I am not sure whether the decode() method that you are calling on the payload is doing anything useful beyond the decoding that get_payload has already done when using decode=True. You should probably test this, and if you find that this call to decode does not do anything (i.e. if body and payload are equal), then you would probably omit this step entirely:

                        body = part.get_payload(decode=True)
                        if body is None:
                            continue
    

    If you add some print statements regarding from_ and subject, you should be able to identify the affected message(s), and then go to "show original" in gmail to compare, and see exactly what is going on.