{"record":{"id":"838fdebc7165f4a6","repo":"golang-migrate/migrate","slug":"conn-v-db-v-838fde","errorCode":null,"errorMessage":"conn: %v, db: %v","messagePattern":"conn: (.+?), db: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"database/pgx/v5/pgx.go","lineNumber":215,"sourceCode":"\t\tMigrationsTable:       migrationsTable,\n\t\tMigrationsTableQuoted: migrationsTableQuoted,\n\t\tStatementTimeout:      time.Duration(statementTimeout) * time.Millisecond,\n\t\tMultiStatementEnabled: multiStatementEnabled,\n\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\tdbErr := p.db.Close()\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\t\treturn nil","sourceCodeStart":197,"sourceCodeEnd":233,"githubUrl":"https://github.com/golang-migrate/migrate/blob/01a9643f1475e75bb6d6224ddeaf9d8e2434ca8a/database/pgx/v5/pgx.go#L197-L233","documentation":"Postgres.Close in the pgx/v5 driver closes both the pgx connection and the underlying database/sql pool. If either returns an error, it reports both values together as 'conn: %v, db: %v'. Even a nil error prints as 'conn: <nil>' because the message always includes both sides.","triggerScenarios":"Calling Close() on a driver obtained from WithInstance/WithConnection/Open when the pgx conn.Close() or db (sql.DB) Close() fails — e.g. the connection is already closed, the network dropped, or the pool was closed by the caller previously.","commonSituations":"Double-closing: application closes the sql.DB it passed to WithConnection and then calls driver.Close(); abrupt network disconnects during shutdown; leaking the driver without ever calling Close (opposite problem) so resources exhaust.","solutions":["Read both parts of the message: the side showing <nil> succeeded, the other side names the real failure","Do not close the *sql.DB yourself if you handed it to WithConnection — let the driver own it","Call Close exactly once per driver instance; ignore/handle 'already closed' errors defensively","If the error indicates a network problem, retry is usually pointless — the migration run is over; just log it"],"exampleFix":"// before\ndb.Close()\n_ = d.Close() // may fail: conn already closed\n// after\nif err := d.Close(); err != nil {\n    log.Printf(\"driver close: %v\", err)\n} // driver owns db; do not close db separately","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"if err := d.Close(); err != nil {\n    // message is 'conn: <x>, db: <y>'; nil side prints <nil>\n    if !strings.Contains(err.Error(), \"already closed\") {\n        log.Printf(\"driver close error: %v\", err)\n    }\n}","preventionTips":["Call Close exactly once per driver instance","Do not close the *sql.DB yourself after passing it to WithConnection","Defer Close and log (not fatal) — close errors after a successful run are usually benign"],"tags":["resource-cleanup","postgres","connection-lifecycle"],"backgroundTag":"connection-close-error","analyzedSha":"01a9643f1475e75bb6d6224ddeaf9d8e2434ca8a","analyzedAt":"2026-09-02T19:38:29.671Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T02:17:09.455Z"}