{"record":{"id":"c64fd3e72d882862","repo":"golang-migrate/migrate","slug":"session-is-closed","errorCode":null,"errorMessage":"session is closed","messagePattern":"session is closed","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"database/cassandra/cassandra.go","lineNumber":35,"sourceCode":"\nfunc init() {\n\tdb := new(Cassandra)\n\tdatabase.Register(\"cassandra\", db)\n}\n\nvar (\n\tmultiStmtDelimiter = []byte(\";\")\n\n\tDefaultMultiStatementMaxSize = 10 * 1 << 20 // 10 MB\n)\n\nvar DefaultMigrationsTable = \"schema_migrations\"\n\nvar (\n\tErrNilConfig     = errors.New(\"no config\")\n\tErrNoKeyspace    = errors.New(\"no keyspace provided\")\n\tErrDatabaseDirty = errors.New(\"database is dirty\")\n\tErrClosedSession = errors.New(\"session is closed\")\n)\n\ntype Config struct {\n\tMigrationsTable       string\n\tKeyspaceName          string\n\tMultiStatementEnabled bool\n\tMultiStatementMaxSize int\n}\n\ntype Cassandra struct {\n\tsession  *gocql.Session\n\tisLocked atomic.Bool\n\n\t// Open and WithInstance need to guarantee that config is never nil\n\tconfig *Config\n}\n\nfunc WithInstance(session *gocql.Session, config *Config) (database.Driver, error) {","sourceCodeStart":17,"sourceCodeEnd":53,"githubUrl":"https://github.com/golang-migrate/migrate/blob/01a9643f1475e75bb6d6224ddeaf9d8e2434ca8a/database/cassandra/cassandra.go#L17-L53","documentation":"ErrClosedSession is returned by WithInstance when the provided *gocql.Session has already been closed (session.Closed() is true). The driver needs a live session to query and update the migrations table, so it rejects a closed one instead of failing later with cryptic gocql errors.","triggerScenarios":"Calling cassandra.WithInstance(session, config) after cluster.Session() result was closed via session.Close(), or reusing a session captured before a shutdown/defer Close ran.","commonSituations":"defer session.Close() at the top of a function followed by driver setup, application shutdown hooks re-running migrations, or tests sharing a session closed in a previous teardown.","solutions":["Create (or re-create) the gocql session after closing: session, err := cluster.CreateSession() and pass the fresh session to WithInstance.","Reorder code so session.Close() runs only after migrations are done (move defer Close to after migration completion).","Check session.Closed() yourself before calling WithInstance and surface a clear message."],"exampleFix":"// before\nsession.Close()\ndriver, err := cassandra.WithInstance(session, cfg)\n// after\ndriver, err := cassandra.WithInstance(session, cfg)\n// ... use driver ...\nsession.Close()","handlingStrategy":"type-guard","validationCode":"if session.Closed() {\n    return fmt.Errorf(\"cannot run migrations: gocql session is already closed\")\n}","typeGuard":"func sessionAlive(s *gocql.Session) bool { return s != nil && !s.Closed() }","tryCatchPattern":"driver, err := cassandra.WithInstance(session, cfg)\nif errors.Is(err, cassandra.ErrClosedSession) {\n    return fmt.Errorf(\"session closed before migrations; create it after connection setup\")\n}","preventionTips":["Place session.Close() only after all migration work completes.","Avoid `defer session.Close()` at the top of a function that also runs migrations.","Do not share one session across setup and teardown phases in tests."],"tags":["go","cassandra","session","go-migrate"],"backgroundTag":"connection-closed","analyzedSha":"01a9643f1475e75bb6d6224ddeaf9d8e2434ca8a","analyzedAt":"2026-09-02T19:38:29.671Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T02:17:09.455Z"}