{"id":"85f93e778eb562b7","repo":"go-redis/redis","slug":"redis-connection-not-available-for-write-operatio","errorCode":null,"errorMessage":"redis: connection not available for write operation","messagePattern":"redis: connection not available for write operation","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"internal/pool/conn.go","lineNumber":28,"sourceCode":"\t\"sync\"\n\t\"sync/atomic\"\n\t\"time\"\n\n\t\"github.com/redis/go-redis/v9/internal\"\n\t\"github.com/redis/go-redis/v9/internal/maintnotifications/logs\"\n\t\"github.com/redis/go-redis/v9/internal/proto\"\n\tuberatomic \"go.uber.org/atomic\"\n)\n\nvar noDeadline = time.Time{}\n\n// Preallocated errors for hot paths to avoid allocations\nvar (\n\terrAlreadyMarkedForHandoff  = errors.New(\"connection is already marked for handoff\")\n\terrNotMarkedForHandoff      = errors.New(\"connection was not marked for handoff\")\n\terrHandoffStateChanged      = errors.New(\"handoff state changed during marking\")\n\terrConnectionNotAvailable   = errors.New(\"redis: connection not available\")\n\terrConnNotAvailableForWrite = errors.New(\"redis: connection not available for write operation\")\n)\n\n// getCachedTimeNs returns the current time in nanoseconds.\n// This function previously used a global cache updated by a background goroutine,\n// but that caused unnecessary CPU usage when the client was idle (ticker waking up\n// the scheduler every 50ms). We now use time.Now() directly, which is fast enough\n// on modern systems (vDSO on Linux) and only adds ~1-2% overhead in extreme\n// high-concurrency benchmarks while eliminating idle CPU usage.\nfunc getCachedTimeNs() int64 {\n\treturn time.Now().UnixNano()\n}\n\n// GetCachedTimeNs returns the current time in nanoseconds.\n// Exported for use by other packages that need fast time access.\nfunc GetCachedTimeNs() int64 {\n\treturn getCachedTimeNs()\n}\n","sourceCodeStart":10,"sourceCodeEnd":46,"githubUrl":"https://github.com/go-redis/redis/blob/36d97525cd8076aed67cddf54778e9ea84550929/internal/pool/conn.go#L10-L46","documentation":"errConnNotAvailableForWrite is a preallocated error (conn.go:28) returned by Conn.WithWriter when the underlying net.Conn is nil at write time (conn.go:1083-1090). It indicates the connection was concurrently closed/handed off while a write was attempted. As with the read variant, the pool treats this as a bad connection and the operation is retried on a new one.","triggerScenarios":"A command write racing with connection close or maintenance-notification handoff; the netConn atomic slot is empty when SetWriteDeadline is attempted.","commonSituations":"Maintenance operations, abrupt connection eviction, or using a connection after the pool removed it under load.","solutions":["Rely on go-redis automatic retry for retriable commands.","Retry idempotent writes at the application level if the error surfaces.","Confirm RESP3 + maintenance notifications are enabled so writes during handoff are buffered/seamless."],"exampleFix":"// before\nerr := client.Set(ctx, key, val, 0).Err()\nif err != nil { return err }\n// after\nfor i := 0; i < 3; i++ {\n    err := client.Set(ctx, key, val, 0).Err()\n    if err == nil { break }\n    if isBadConn(err, true) { continue }\n}","handlingStrategy":"retry","validationCode":"// No deterministic pre-check (race with close). Tune pool + maintnotifications\nto minimize abrupt net.Conn removal during writes.","typeGuard":null,"tryCatchPattern":"for i := 0; i < 3; i++ {\n    err := client.Set(ctx, key, val, ttl).Err()\n    if err == nil { break }\n    if isBadConn(err, true) { continue }\n    return err\n}","preventionTips":["Enable RESP3 + maintenance notifications for graceful write-path handoff.","Avoid writes during planned maintenance if possible.","Size the pool to avoid emergency eviction mid-write."],"tags":["pool","connection","transient","maintnotifications","retry","write"],"analyzedSha":"36d97525cd8076aed67cddf54778e9ea84550929","analyzedAt":"2026-08-06T01:08:27.376Z","schemaVersion":2}