redis/redis ยท error
\nI/O error\n
Error message
\nI/O error\n
What it means
Printed by cliAuth() in src/redis-cli.c:1620 when redisCommand(ctx, "AUTH ...") returns a NULL reply, meaning the hiredis context is already in an error state before the server could respond to AUTH. It is not an authentication rejection (that path produces error 71) but a transport-level failure: the underlying socket broke, timed out, or the connection's read/write failed while the AUTH command was in flight.
Source
Thrown at src/redis-cli.c:1620
tcsetattr(STDIN_FILENO, TCSANOW, &mode);
}
/*------------------------------------------------------------------------------
* Networking / parsing
*--------------------------------------------------------------------------- */
/* Send AUTH command to the server */
static int cliAuth(redisContext *ctx, char *user, char *auth) {
redisReply *reply;
if (auth == NULL) return REDIS_OK;
if (user == NULL)
reply = redisCommand(ctx,"AUTH %s",auth);
else
reply = redisCommand(ctx,"AUTH %s %s",user,auth);
if (reply == NULL) {
fprintf(stderr, "\nI/O error\n");
return REDIS_ERR;
}
int result = REDIS_OK;
if (reply->type == REDIS_REPLY_ERROR) {
result = REDIS_ERR;
fprintf(stderr, "AUTH failed: %s\n", reply->str);
}
freeReplyObject(reply);
return result;
}
/* Send SELECT input_dbnum to the server */
static int cliSelect(void) {
redisReply *reply;
if (config.conn_info.input_dbnum == config.dbnum) return REDIS_OK;
reply = redisCommand(context,"SELECT %d",config.conn_info.input_dbnum);View on GitHub (pinned to 3acc0c49cf)
Solutions
- Verify the server is still running and reachable right before the client connects: redis-cli ping or redis-cli -h <host> -p <port> ping.
- Check that no proxy or firewall between client and server is terminating the connection mid-handshake; increase idle/proxy timeouts.
- Reproduce with a longer connect timeout (--connect-timeout) to rule out REDIS_ERR_TIMEOUT masquerading as I/O error.
- If using TLS, confirm the TLS negotiation succeeded first (error 78 would normally surface, but a half-broken context can leak here).
- File a reproducible packet capture if the socket genuinely closes between accept and AUTH.
Example fix
// before: implicit short timeout may drop mid-AUTH redis-cli -h host -p 6379 -a secret // after: explicit, generous connect timeout + retry redis-cli -h host -p 6379 -a secret --connect-timeout 30
Defensive patterns
Strategy: retry
Validate before calling
import socket
import redis
def assert_reachable(host, port, timeout=2.0):
# Cheap liveness probe before issuing AUTH so a dropped socket
# is caught as a connect error, not an I/O error mid-handshake.
try:
with socket.create_connection((host, port), timeout=timeout):
return True
except OSError:
return False
# Call before constructing the client:
if not assert_reachable(redis_host, redis_port):
raise ConnectionError(f"Redis {redis_host}:{redis_port} unreachable")
client = redis.Redis(host=redis_host, port=redis_port, socket_keepalive=True,
socket_connect_timeout=2.0, socket_timeout=2.0,
health_check_interval=30) Try / catch
# An I/O error on AUTH means the server closed/reset the socket during the
# handshake (reply == NULL). Treat as transient: reconnect + retry with
# bounded exponential backoff.
from redis.exceptions import ConnectionError as RedisConnectionError
import time
def auth_with_retry(factory, password, attempts=5, base=0.1):
last = None
for i in range(attempts):
client = factory()
try:
client.ping()
return client
except RedisConnectionError as e:
last = e
time.sleep(base * (2 ** i))
raise last
Prevention
- Keep AUTH credentials non-empty and correctly scoped; an empty/wrong password is the most common cause of a reset right after AUTH.
- Enable socket_keepalive and a short socket_timeout so a half-open socket fails fast instead of producing a mid-handshake I/O error.
- Set socket_connect_timeout distinctly from socket_timeout so a dead host is detected during connect, before AUTH is ever sent.
- Validate reachability with a TCP probe before constructing the client when operating over an unstable network.
- Use a connection pool with a health_check_interval so stale connections are reaped before AUTH rather than failing on it.
When it happens
Trigger: cliAuth() is invoked during cliConnect() (line 1771) right after a TCP/unix socket is established. The NULL reply occurs when config.conn_info.auth is non-NULL and the hiredis context has context->err set (REDIS_ERR_IO, REDIS_ERR_EOF, or REDIS_ERR_TIMEOUT) at the moment redisCommand() is called.
Common situations: Server killed or restarted between accept() and AUTH; a proxy/load-balancer (e.g. Envoy, HAProxy) closing idle connections before AUTH completes; TLS handshake that left the context in an error state but was not caught; network partition on a slow link where the connection succeeds TCP-wise but drops before the first command round-trip.
Related errors
- Could not connect to Redis at %s:%d: %s\n
- Could not connect to Redis at %s:%d: %s
- Could not connect to Redis at %s: %s\n
- I/O error
- Node %s:%d replied with error: %s
AI-assisted analysis of redis/redis@3acc0c49cf (2026-08-01).
Data as JSON: /data/errors/a9056da91a844bd4.json.
Report an issue: GitHub.