A few months ago, Microsoft forced everyone who was programmatically fetching emails from Microsoft’s hosted email to switch from the standard SMTP protocol to their APIs instead.
Anyway, it was working pretty well for a while. But then the
o365 lib started throwing some random 404 client errors which looked like this:
HTTPError: 404 Client Error: Not Found for url: https://firstname.lastname@example.org/messages/some-long-base64-encoded-string-here==/$value | Error Message: The specified object was not found in the store.
Due to this error some imported attachments were empty.
It turns out Microsoft’s Office 365 Advanced Threat Protection (ATP) was the culprit. When ATP is ON you can see the emails immediately after they have been received, you can also see how many attachments each email has and even see the attachment file names. But as long as the ATP scan is in progress you cannot download the attachments. The web interface shows an “ATP Scan in progress” message, but the API client sees only that the attachments are empty.
As a quick fix you can wait e.g. 10 minutes before fetching the newly arrived emails. Using the
o365 library it looks like this:
import datetime as dt from O365 import Account credentials = ('client_id', 'client_secret') account = Account(credentials, auth_flow_type='credentials', tenant_id='tenant_id') if account.authenticate(): print('Authenticated!') mailbox = account.mailbox(resource="email@example.com") inbox = mailbox.inbox_folder() # wait 10 minutes before fetching emails, due to "ATP Scan in progress" # some attachments are empty when fetched while the scan is still running query = inbox.q('received_date_time').less(dt.datetime.now() - dt.timedelta(minutes=10)) for message in inbox.get_messages(query=query): print(message)
Alternatively, you could try to read the
deviceThreatProtectionLevel status to determine if an email with attachments is ready for a download or not, but at the time of this writing I could not see a way to access this information from the python client.
This is not THE solution for every graphql
HTTPError: 404 Client Error error, but if you get empty attachments give it a try, it might be due to ATP.