{"record":{"id":"c894d3ac2b419f7f","repo":"kataras/iris","slug":"redis-already-closed","errorCode":null,"errorMessage":"redis: already closed","messagePattern":"redis: already closed","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"sessions/sessiondb/redis/database.go","lineNumber":309,"sourceCode":"\tif err != nil {\n\t\tdb.logger.Debugf(\"Database.Release.Driver.Delete: %s: %v\", sid, err)\n\t}\n\n\treturn err\n}\n\n// Close terminates the redis connection.\nfunc (db *Database) Close() error {\n\treturn closeDB(db)\n}\n\nfunc closeDB(db *Database) error {\n\treturn db.c.Driver.CloseConnection()\n}\n\nvar (\n\t// ErrRedisClosed an error with message 'redis: already closed'\n\tErrRedisClosed = errors.New(\"redis: already closed\")\n\t// ErrKeyNotFound a type of error of non-existing redis keys.\n\t// The producers(the library) of this error will dynamically wrap this error(fmt.Errorf) with the key name.\n\t// Usage:\n\t// if err != nil && errors.Is(err, ErrKeyNotFound) {\n\t// [...]\n\t// }\n\tErrKeyNotFound = errors.New(\"key not found\")\n)\n","sourceCodeStart":291,"sourceCodeEnd":318,"githubUrl":"https://github.com/kataras/iris/blob/7bedaf55a0b64bbb2248a5845a2c60d81a30996a/sessions/sessiondb/redis/database.go#L291-L318","documentation":"ErrRedisClosed is a sentinel error ('redis: already closed') returned when a redis session database operation is attempted after the redis driver connection has already been terminated via Database.Close(). The underlying redis client refuses to run commands on a closed connection. It signals a lifecycle bug in the caller, not a redis server problem.","triggerScenarios":"Calling Acquire/Update/Release/other Database methods after calling Database.Close(); closing the sessiondb (e.g. on app shutdown) while a background goroutine or in-flight request still touches the session store.","commonSituations":"Graceful shutdown handlers that close redis before all workers finish; double-Close in defer chains; using a session Database across application restarts where it was closed during teardown.","solutions":["Ensure Database.Close() is called only after all session traffic has stopped (close it last in the shutdown sequence)","Guard shutdown ordering: wait for in-flight requests to drain before closing the redis session DB","Remove duplicate Close calls (e.g. both defer Close and explicit Close)"],"exampleFix":"// before\ndb.Close()\ngo session.Update(sid, values)\n// after\nsession.Update(sid, values)\ndb.Close()","handlingStrategy":"type-guard","validationCode":"if db.IsClosed() { return errors.New(\"session store closed\") }\n_ = db.Ping() // pre-check connectivity","typeGuard":"func isRedisClosedErr(err error) bool { return errors.Is(err, redisdb.ErrRedisClosed) }","tryCatchPattern":"if err := db.Update(sid, values); err != nil {\n    if errors.Is(err, redisdb.ErrRedisClosed) {\n        // skip/queue: store shut down\n        return nil\n    }\n    return err\n}","preventionTips":["Close the session database last in shutdown, after draining in-flight requests","Never call Close twice; track closed state once","Use defer Close only at the very top-level owner of the connection"],"tags":["redis","sessions","lifecycle"],"backgroundTag":"client-already-closed","analyzedSha":"7bedaf55a0b64bbb2248a5845a2c60d81a30996a","analyzedAt":"2026-08-30T20:38:16.250Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}