Skip to content

Fall back to RESP2 when HELLO is not supported by server/proxy#4090

Open
yangbodong22011 wants to merge 2 commits into
redis:masterfrom
yangbodong22011:fix/hello-fallback-resp2
Open

Fall back to RESP2 when HELLO is not supported by server/proxy#4090
yangbodong22011 wants to merge 2 commits into
redis:masterfrom
yangbodong22011:fix/hello-fallback-resp2

Conversation

@yangbodong22011

@yangbodong22011 yangbodong22011 commented May 29, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes #4089

redis-py 8.0 defaults to RESP3, which sends a HELLO command during connection handshake. If the server (Redis < 6.0) or proxy doesn't support HELLO, the connection fails with ResponseError: unknown command — there is no fallback.

This PR adds a try/except ResponseError around the HELLO response in both sync and async connection paths. On failure, it gracefully falls back to RESP2 with standalone AUTH, matching the behavior of Lettuce, Jedis, and go-redis.

Changes

  • redis/connection.pyon_connect_check_health(): catch ResponseError after HELLO, revert parser to RESP2, issue standalone AUTH
  • redis/asyncio/connection.py — same fix for the async path

Both branch 1 (RESP3 + auth) and branch 3 (RESP3 + no auth) are covered.

Testing

Manually verified against a proxy that rejects HELLO with ERR unknown command. Connection now succeeds with automatic RESP2 fallback instead of raising.


Note

Medium Risk
Changes connection handshake and authentication for all default-RESP3 clients; behavior is gated on error text and explicit protocol/maint settings, but misclassified errors could still affect connect success.

Overview
When the default RESP3 connect path sends HELLO and the server or proxy rejects it (unknown command / NOPROTO), sync and async connections now downgrade to RESP2 instead of failing outright. Shared helpers _fallback_to_resp2 and _do_resp2_auth centralize parser/protocol downgrade, optional standalone AUTH with fresh credentials (avoiding HELLO’s injected default username), and a warning unless the user set protocol=3 explicitly or maintenance notifications are enabled.

Successful HELLO responses are stored in handshake_metadata on both paths; after fallback it is set to {} so downstream code does not see None. New tests in tests/test_hello_fallback.py cover auth branches, NOPROTO, no-fallback cases, and logging.

Reviewed by Cursor Bugbot for commit 9ce4953. Bugbot is set up for automated code reviews on this repo. Configure here.

@jit-ci

jit-ci Bot commented May 29, 2026

Copy link
Copy Markdown

Hi, I’m Jit, a friendly security platform designed to help developers build secure applications from day zero with an MVS (Minimal viable security) mindset.

In case there are security findings, they will be communicated to you as a comment inside the PR.

Hope you’ll enjoy using Jit.

Questions? Comments? Want to learn more? Get in touch with us.

@yangbodong22011 yangbodong22011 force-pushed the fix/hello-fallback-resp2 branch from 10806a7 to 0fc4c80 Compare May 29, 2026 07:44
Comment thread redis/connection.py Outdated
@yangbodong22011 yangbodong22011 force-pushed the fix/hello-fallback-resp2 branch 2 times, most recently from b9cec38 to 0753043 Compare May 29, 2026 08:07
Comment thread redis/connection.py Outdated
@petyaslavova

Copy link
Copy Markdown
Collaborator

Hey @yangbodong22011, thank you for the contribution! I’ll review it shortly.

Meanwhile, can you please check Cursor’s comment and the test failures?

There are also a few things we need to be careful about when changing the protocol configuration at this point in the flow: what happens if the parser is HiredisParser, which callbacks would be assigned later for command response post-processing, and how this interacts with maintenance notifications and client-side caching configuration.

Having dedicated unit tests for those cases would be very helpful.

@yangbodong22011 yangbodong22011 force-pushed the fix/hello-fallback-resp2 branch from 0753043 to 915ac57 Compare June 1, 2026 06:19
Comment thread redis/asyncio/connection.py Outdated
@yangbodong22011 yangbodong22011 force-pushed the fix/hello-fallback-resp2 branch from 915ac57 to 4d21d3a Compare June 1, 2026 06:38
Comment thread redis/connection.py Outdated
@yangbodong22011 yangbodong22011 force-pushed the fix/hello-fallback-resp2 branch from 4d21d3a to 07e0652 Compare June 1, 2026 06:55
Comment thread redis/connection.py Outdated
When the default protocol is RESP3 (8.0+), servers or proxies that do
not support HELLO will now gracefully fall back to RESP2 + standalone
AUTH instead of raising an exception.

Key design decisions:
- Only fall back for "unknown command" or "NOPROTO" errors; other
  errors (WRONGPASS, NOAUTH, etc.) propagate immediately.
- Only auto-fallback when protocol was NOT explicitly set by the user
  and maintenance notifications are not force-enabled (enabled=True),
  since those features depend on RESP3 push channels.
- Extract _do_resp2_auth() helper to avoid duplicating the AUTH +
  Redis <6.0 single-arg fallback logic (issue redis#1274).
- The helper re-fetches credentials from the provider (not the
  HELLO-mutated auth_args with injected "default"), saving one
  round-trip for password-only configurations.
- Emit a WARNING log with the actual protocol version attempted.

Closes redis#4089

🤖 Generated with [Qoder][https://qoder.com]
@yangbodong22011 yangbodong22011 force-pushed the fix/hello-fallback-resp2 branch from 07e0652 to e1a72da Compare June 1, 2026 07:43

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

Reviewed by Cursor Bugbot for commit e1a72da. Configure here.

Comment thread redis/connection.py
self.maint_notifications_config
and self.maint_notifications_config.enabled is True
):
raise error

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Fallback doesn't guard against client-side caching configuration

Medium Severity

_fallback_to_resp2 checks for maint_notifications_config (which requires RESP3 push notifications) before allowing the downgrade, but doesn't check for client-side caching configuration. Client-side caching relies on RESP3 push notifications for key invalidation tracking. Silently falling back to RESP2 when caching is configured would cause the cache to never receive invalidation messages, leading to stale data being served indefinitely without any error or warning.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit e1a72da. Configure here.

Comment thread redis/asyncio/connection.py
The async RESP3 branches stored the HELLO response in a local variable
`response` but never assigned it to `self.handshake_metadata`. This
caused downstream code (CacheProxyConnection.connect()) to hit
AttributeError when accessing handshake_metadata.get(...) on None.

Changes:
- Async branch 1 & 3: assign read_response() to self.handshake_metadata
- Add self.handshake_metadata = None in async __init__ (sync had it)
- Add proto version validation to sync branch 1 (was only on branch 3)
- Add proto validation to async branch 3 for full symmetry
- Tests: set driver_info=None to isolate HELLO/AUTH mock flow
- Add 2 regression tests for success-path metadata assignment

🤖 Generated with [Qoder][https://qoder.com]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

RESP3 default in 8.0 breaks connections to servers/proxies that don't support HELLO (no fallback)

2 participants