Skip to content

fix(gateway): add IPv4 fallback for IMAP connections on unreachable IPv6 - #46854

Open
srojk34 wants to merge 1 commit into
NousResearch:mainfrom
srojk34:fix/email-imap-ipv4-fallback
Open

srojk34 wants to merge 1 commit into
NousResearch:mainfrom
srojk34:fix/email-imap-ipv4-fallback

Conversation

@srojk34

@srojk34 srojk34 commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Both IMAP connection sites in plugins/platforms/email/adapter.py (connect() and _fetch_new_messages()) used raw imaplib.IMAP4_SSL, which resolves via the system DNS and connects to whichever address comes first — often IPv6.
  • On dual-stack hosts where the IPv6 address is unreachable (common on networks without IPv6 routing), the connection hangs until socket timeout (30 s) before falling back to the next address, if any.
  • The SMTP path already has _connect_smtp() with _IPv4SMTP / _IPv4SMTP_SSL fallback classes, but IMAP had no equivalent.

Fix

  • Add _IPv4IMAP4_SSL (mirrors _IPv4SMTP_SSL): overrides _create_socket to use _create_ipv4_connection.
  • Add IMAP_CONNECT_TIMEOUT constant (30 s) for consistency with SMTP_CONNECT_TIMEOUT.
  • Add _connect_imap() method: tries default IMAP4_SSL first; on connection-level failure retries with _IPv4IMAP4_SSL. TLS verification errors are not retried.
  • Replace both raw imaplib.IMAP4_SSL(...) call sites with self._connect_imap().

Validation

  • pytest tests/gateway/test_email.py -x -q77 passed

Test plan

  • test_default_connection_used_when_reachable — no IPv4 fallback when IMAP4_SSL connects normally
  • test_ipv6_timeout_falls_back_to_ipv4 — timeout triggers IPv4-only retry
  • test_tls_verification_error_does_not_retry_ipv4 — SSLError is re-raised, not retried
  • test_connect_uses_connect_imap — connect() routes through _connect_imap()

@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/gateway Gateway runner, session dispatch, delivery platform/email Email (IMAP/SMTP) adapter labels Jun 15, 2026
Both IMAP call sites (connect test + poll fetch) used raw
imaplib.IMAP4_SSL, so a dual-stack host with unreachable IPv6 hangs
until socket timeout. Add _IPv4IMAP4_SSL and _connect_imap() mirroring
the existing _connect_smtp() pattern.

Rebase after 5600105 moved gateway/platforms/email.py →
plugins/platforms/email/adapter.py.
@srojk34
srojk34 force-pushed the fix/email-imap-ipv4-fallback branch from 0e54540 to 4c0c9f8 Compare June 22, 2026 14:14

@teknium1 teknium1 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for covering both IMAP call sites and adding focused tests.

Problems

  • The added fallback is reached only after imaplib.IMAP4_SSL(...) raises (plugins/platforms/email/adapter.py:424 in the PR diff). IMAP4_SSL uses socket.create_connection, which iterates resolved address candidates; when IPv6 times out but IPv4 then succeeds, the initial call returns successfully after the timeout, so this fallback is never invoked. The reported delay remains.
  • The new timeout test mocks an immediate constructor exception, so it does not exercise that dual-stack IPv6-timeout/IPv4-success sequence. Current main's SMTP helper has the same default-first retry shape at plugins/platforms/email/adapter.py:542-549.

Suggested changes

  • Use a strategy that can choose or race IPv4 before the default sequential connection path returns, while retaining IPv6-only support.
  • Add a controlled dual-address regression test for the IPv6-timeout then IPv4-success case.

Automated hermes-sweeper review.

address that is unreachable, the default connection hangs until socket
timeout. We retry through an IPv4-only socket path. TLS verification
errors are not retried.
"""

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This fallback only runs after the default constructor raises. IMAP4_SSL uses socket.create_connection, which iterates resolved addresses; if IPv6 times out and a later IPv4 candidate succeeds, this call returns after the timeout rather than raising, so _IPv4IMAP4_SSL is never used. Please use a strategy that selects or races IPv4 before that default path can complete, and cover the IPv6-timeout/IPv4-success sequence.

@teknium1 teknium1 added sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform labels Jul 14, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/gateway Gateway runner, session dispatch, delivery P2 Medium — degraded but workaround exists platform/email Email (IMAP/SMTP) adapter sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants