go-redis/redis · error

failed to enable maintnotifications: server does not support

Error message

failed to enable maintnotifications: server does not support RESP3 (HELLO command failed)

What it means

Thrown when maintenance notifications are explicitly required (MaintNotificationsConfig.Mode == ModeEnabled), Protocol is 3, but the HELLO command did not succeed — meaning the connection negotiated RESP2 and cannot receive RESP3 push frames. The client fails the connection fast rather than silently operating without maintenance notifications.

Source

Thrown at redis.go:927

		endpointType = c.opt.MaintNotificationsConfig.EndpointType
		maintNotifMode = c.opt.MaintNotificationsConfig.Mode
	}
	c.optLock.RUnlock()

	// Maintenance notifications require RESP3 push frames. If HELLO failed
	// and the connection fell back to RESP2, there is no point in sending
	// CLIENT MAINT_NOTIFICATIONS: the server either rejects it (making the
	// error misleading) or accepts it silently, leaving the client unable
	// to receive any notifications. Decide based on the actual negotiated
	// protocol rather than the requested one.
	if maintNotifEnabled && protocol == 3 && !helloOK {
		if maintNotifMode == maintnotifications.ModeEnabled {
			// Explicitly requested - fail fast with a clear reason.
			cn.GetStateMachine().Transition(pool.StateClosed)
			if errorCallback := pool.GetMetricErrorCallback(); errorCallback != nil {
				errorCallback(ctx, "HANDSHAKE_FAILED", cn, "HANDSHAKE_FAILED", true, 0)
			}
			return fmt.Errorf("failed to enable maintnotifications: server does not support RESP3 (HELLO command failed)")
		}
		// auto/other modes: silently disable maintnotifications for this client.
		c.optLock.Lock()
		c.opt.MaintNotificationsConfig.Mode = maintnotifications.ModeDisabled
		c.optLock.Unlock()
		if err := c.disableMaintNotificationsUpgrades(); err != nil {
			internal.Logger.Printf(ctx, "failed to disable maintnotifications in auto mode: %v", err)
		}
		maintNotifEnabled = false
	}

	var maintNotifHandshakeErr error
	if maintNotifEnabled && protocol == 3 {
		// Hold the manager read lock across the handshake and tracking so a
		// concurrent downgrade cannot remove pool-level listeners before a
		// successfully enabled connection is tracked for retirement.
		c.maintNotificationsManagerLock.RLock()
		manager := c.maintNotificationsManager

View on GitHub (pinned to 36d97525cd)

Solutions

  1. Switch MaintNotificationsConfig.Mode to ModeAuto (default) or ModeDisabled if the endpoint is known to be RESP2-only — the client will then silently disable maintnotifications instead of failing.
  2. Upgrade the target server to Redis 6+ (preferably 8.x) so HELLO/RESP3 and CLIENT MAINT_NOTIFICATIONS are supported.
  3. Confirm Options.Protocol is actually 3 and not being overridden; remove any proxy that strips RESP3.
  4. Validate connectivity to a RESP3-capable endpoint before constructing the client.

Example fix

// before
cfg := &redis.MaintNotificationsConfig{Mode: redis.ModeEnabled}
opt := &redis.Options{Addr: addr, Protocol: 3, MaintNotificationsConfig: cfg}
// server is RESP2-only -> connection fails

// after
cfg := &redis.MaintNotificationsConfig{Mode: redis.ModeAuto}
opt := &redis.Options{Addr: addr, Protocol: 3, MaintNotificationsConfig: cfg}
Defensive patterns

Strategy: validation

Validate before calling

// Detect RESP3/HELLO support before opting into ModeEnabled.
c := redis.NewClient(&redis.Options{Addr: addr, Protocol: 2})
var info map[string]string
_ = c.Hello(ctx, 3, "", "", "").Scan(&info)
c.Close()
if len(info) == 0 {
    return errors.New("server does not support RESP3; use ModeAuto/ModeDisabled")
}

Try / catch

defer func() {
    if r := recover(); r != nil {
        if strings.Contains(fmt.Sprint(r), "does not support RESP3") {
            // downgrade config and retry
        }
    }
}()
client := redis.NewClient(opt)

Prevention

When it happens

Trigger: Options.MaintNotificationsConfig.Mode = ModeEnabled, Options.Protocol = 3, and the server rejects or does not support HELLO (redis < 6, DragonflyDB, a proxy, or RESP2-only endpoint). Returned on every dial since maintnotification handshake is part of initConn.

Common situations: Pointing a ModeEnabled client at an older Redis (< 6) that has no HELLO; pointing at DragonflyDB or a proxy that speaks RESP2; connecting through a RESP2-terminating load balancer; REDIS_VERSION mismatch between configured image and actual server.

Related errors


AI-assisted analysis of go-redis/redis@36d97525cd (2026-08-06). Data as JSON: /data/errors/e98e75eb41ccec46.json. Report an issue: GitHub.