infiniflow/ragflow · error · RuntimeError
Failed to fetch email ids; {status=}
Error message
Failed to fetch email ids; {status=} What it means
Raised by _fetch_email_ids_in_mailbox when mail_client.search returns a non-OK status or an empty response list. The date-window search (SINCE/BEFORE built from the start/end epoch args) failed at the protocol level. Note that an empty-but-OK search (no mail in range) returns normally - this error means the command itself failed.
Source
Thrown at common/data_source/imap_connector.py:450
mailbox: str,
start: SecondsSinceUnixEpoch,
end: SecondsSinceUnixEpoch,
) -> list[str]:
if not _select_mailbox(mail_client, mailbox):
logging.warning(f"Skip mailbox: {mailbox}")
return []
start_dt = datetime.fromtimestamp(start, tz=timezone.utc)
end_dt = datetime.fromtimestamp(end, tz=timezone.utc) + timedelta(days=1)
start_str = start_dt.strftime("%d-%b-%Y")
end_str = end_dt.strftime("%d-%b-%Y")
search_criteria = f'(SINCE "{start_str}" BEFORE "{end_str}")'
status, email_ids_byte_array = mail_client.search(None, search_criteria)
if status != _IMAP_OKAY_STATUS or not email_ids_byte_array:
raise RuntimeError(f"Failed to fetch email ids; {status=}")
email_ids: bytes = email_ids_byte_array[0]
return [email_id.decode() for email_id in email_ids.split()]
def _fetch_email(mail_client: imaplib.IMAP4_SSL, email_id: str) -> Message | None:
status, msg_data = mail_client.fetch(message_set=email_id, message_parts="(RFC822)")
if status != _IMAP_OKAY_STATUS or not msg_data:
return None
data = msg_data[0]
if not isinstance(data, tuple):
raise RuntimeError(f"Message data should be a tuple; instead got a {type(data)=} {data=}")
_, raw_email = data
return email.message_from_bytes(raw_email)
View on GitHub (pinned to 554fb1133a)
Solutions
- Retry with a freshly created mail client (re-login) and re-select the mailbox before searching.
- Verify the mailbox still exists immediately before searching if folders change frequently.
- Serialize access so only one operation uses an IMAP connection at a time.
- Reproduce the SEARCH manually to see the server's response text if failures are persistent.
Example fix
# before
ids = _fetch_email_ids_in_mailbox(client, mailbox, start, end) # RuntimeError mid-sync
# after
try:
ids = _fetch_email_ids_in_mailbox(client, mailbox, start, end)
except RuntimeError:
client = connector._get_mail_client()
client.select(mailbox, readonly=True)
ids = _fetch_email_ids_in_mailbox(client, mailbox, start, end) Defensive patterns
Strategy: retry
Try / catch
try:
ids = _fetch_email_ids_in_mailbox(client, mailbox, start, end)
except RuntimeError as e:
if 'Failed to fetch email ids' not in str(e):
raise
client = connector._get_mail_client()
client.select(mailbox, readonly=True)
ids = _fetch_email_ids_in_mailbox(client, mailbox, start, end) Prevention
- Re-select the mailbox after any reconnect before issuing SEARCH.
- Run one operation per IMAP connection at a time; sessions are not concurrency-safe.
- Bound retries with backoff so a persistent server failure surfaces instead of looping.
When it happens
Trigger: Session dropped or mailbox deselected before SEARCH; the server rejects the search criteria format; server under load returning 'NO'. Triggered per-mailbox during checkpoint processing when message ids need to be enumerated.
Common situations: Intermittent disconnects on long syncs; servers rejecting the quoted date format due to locale differences; two jobs sharing one IMAP connection concurrently; the mailbox being deleted or renamed between LIST and SEARCH.
Related errors
- Failed to fetch mailboxes; {status=}
- Failed to log into imap server; {status=}
- Credential item {name=} was not found
- Credential item {name=} must be of type str, instead receive
- Failed to find any mailboxes for this email account
AI-assisted analysis of infiniflow/ragflow@554fb1133a (2026-08-15).
Data as JSON: /api/errors/9e63bf7533844b35.
Report an issue: GitHub.