{"record":{"id":"5598c16d271457dc","repo":"golang-migrate/migrate","slug":"conn-v-db-v-5598c1","errorCode":null,"errorMessage":"conn: %v, db: %v","messagePattern":"conn: (.+?), db: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"database/postgres/postgres.go","lineNumber":227,"sourceCode":"\t\tMultiStatementMaxSize: multiStatementMaxSize,\n\t})\n\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn px, nil\n}\n\nfunc (p *Postgres) Close() error {\n\tconnErr := p.conn.Close()\n\tvar dbErr error\n\tif p.db != nil {\n\t\tdbErr = p.db.Close()\n\t}\n\n\tif connErr != nil || dbErr != nil {\n\t\treturn fmt.Errorf(\"conn: %v, db: %v\", connErr, dbErr)\n\t}\n\treturn nil\n}\n\n// https://www.postgresql.org/docs/9.6/static/explicit-locking.html#ADVISORY-LOCKS\nfunc (p *Postgres) Lock() error {\n\treturn database.CasRestoreOnErr(&p.isLocked, false, true, database.ErrLocked, func() error {\n\t\taid, err := database.GenerateAdvisoryLockId(p.config.DatabaseName, p.config.migrationsSchemaName, p.config.migrationsTableName)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t// This will wait indefinitely until the lock can be acquired.\n\t\tquery := `SELECT pg_advisory_lock($1)`\n\t\tif _, err := p.conn.ExecContext(context.Background(), query, aid); err != nil {\n\t\t\treturn &database.Error{OrigErr: err, Err: \"try lock failed\", Query: []byte(query)}\n\t\t}\n","sourceCodeStart":209,"sourceCodeEnd":245,"githubUrl":"https://github.com/golang-migrate/migrate/blob/01a9643f1475e75bb6d6224ddeaf9d8e2434ca8a/database/postgres/postgres.go#L209-L245","documentation":"Close() on the Postgres driver closes both the underlying *sql.DB and the raw *sql.Conn. If either close returns an error, both errors are combined into one 'conn: %v, db: %v' message, including nil for whichever side succeeded. It indicates the database resources may not have been released cleanly.","triggerScenarios":"Calling Driver.Close() when the TCP connection to PostgreSQL was already broken, the server rejected the termination, or the database handle was already closed.","commonSituations":"Closing the migrator after a network drop or server restart; double-closing resources because the caller also closed the *sql.DB it passed to WithInstance; timeouts during shutdown.","solutions":["Inspect both sub-errors in the message; if one says 'sql: database is closed' the caller closed the handle first — only close one side","Check network/firewall stability to the PostgreSQL server and retry Close once after reconnect logic if needed","Treat non-nil conn/db errors during process shutdown as warnings if the process is exiting and the OS will reclaim sockets"],"exampleFix":"// before\nif err := driver.Close(); err != nil { panic(err) }\n// after\nif err := driver.Close(); err != nil {\n    if !errors.Is(err, sql.ErrConnDone) && !strings.Contains(err.Error(), \"database is closed\") {\n        log.Printf(\"migrator close failed: %v\", err)\n    }\n}","handlingStrategy":"try-catch","validationCode":"if instance.db == nil || instance.conn == nil {\n    return errors.New(\"migrator already closed or not fully initialized\")\n}","typeGuard":null,"tryCatchPattern":"err := driver.Close()\nif err != nil {\n    var parts struct{ conn, db error }\n    if _, scanErr := fmt.Sscanf(err.Error(), \"conn: %v, db: %v\", &parts.conn, &parts.db); scanErr == nil {\n        if !strings.Contains(err.Error(), \"sql: database is closed\") {\n            log.Printf(\"unexpected close error: %v\", err)\n        }\n    }\n}","preventionTips":["Give the migrator sole ownership of the *sql.DB you pass to WithInstance","Avoid calling Close twice; track closed state with sync.Once","Treat close errors during process exit as best-effort warnings"],"tags":["postgres","resource-cleanup","connection-close"],"backgroundTag":"connection-close-error","analyzedSha":"01a9643f1475e75bb6d6224ddeaf9d8e2434ca8a","analyzedAt":"2026-09-02T19:38:29.671Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T02:17:09.455Z"}