imaplib.error: command FETCH illegal in state AUTH

HankSmackHood picture HankSmackHood · Mar 4, 2011 · Viewed 7.5k times · Source

I'm trying to download attachments from Gmail, using a combination of pieces of code I found online, and some editing from myself. However, the following code:

import email, getpass, imaplib, os, random, time
import oauth2 as oauth
import oauth2.clients.imap as imaplib

MY_EMAIL = '[email protected]'
MY_TOKEN = "token"
MY_SECRET = "secret"

consumer = oauth.Consumer('anonymous', 'anonymous')
token = oauth.Token(MY_TOKEN, MY_SECRET)

url = "https://mail.google.com/mail/b/"+MY_EMAIL+"/imap/"
m = imaplib.IMAP4_SSL('imap.gmail.com')

m.authenticate(url, consumer, token)

m.select('INBOX')

items = m.select("UNSEEN"); 
items = items[0].split() 

for emailid in items:
    data = m.fetch(emailid, "(RFC822)")

returns this error:

imaplib.error: command FETCH illegal in state AUTH

Why would Fetch be illegal while I'm authorized?

Answer

g.d.d.c picture g.d.d.c · Mar 4, 2011

You're lacking error checking on your calls to select. Typically, this is how I'll structure the first parts of a connection to a mailbox:

# self.conn is an instance of IMAP4 connected to my server.
status, msgs = self.conn.select('INBOX')

if status != 'OK':
  return # could be break, or continue, depending on surrounding code.

msgs = int(msgs[0])

Essentially, the trouble you're encountering is that you've selected a mailbox that doesn't exist, your status message is probably not "OK" as it should be, and the value you're iterating over isn't valid. Remember, select expects a mailbox name. It does not search based on a flag (which may be what you're attempting with "UNSEEN"). When you select a non-existent mail box you actually get this as a response:

('NO', ['The requested item could not be found.'])

In which case, for email id in items is not operating properly. Not what you're after in any way, unfortunately. What you'd get on a valid mailbox would be like this:

('OK', ['337'])

Hope that helps.

To address the question in comments, if you want to actually retrieve the unseen messages in the mailbox you'd use this:

status, msgs = self.conn.select('INBOX')
# returns ('OK', ['336'])

status, ids = self.conn.search(None, 'UNSEEN')
# returns ('OK', ['324 325 326 336'])

if status == 'OK':
  ids = map(int, ids[0].split())

The response is going to be similar to the response from select, but instead of a single integer for the number of messages you'll get a list of ids.