{"record":{"id":"8521e9150baa95df","repo":"golang-migrate/migrate","slug":"conn-v-db-v-8521e9","errorCode":null,"errorMessage":"conn: %v, db: %v","messagePattern":"conn: (.+?), db: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"database/sqlserver/sqlserver.go","lineNumber":187,"sourceCode":"\n\tpx, err := WithInstance(db, &Config{\n\t\tDatabaseName:    purl.Path,\n\t\tMigrationsTable: migrationsTable,\n\t})\n\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn px, nil\n}\n\n// Close the database connection\nfunc (ss *SQLServer) Close() error {\n\tconnErr := ss.conn.Close()\n\tdbErr := ss.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// Lock creates an advisory local on the database to prevent multiple migrations from running at the same time.\nfunc (ss *SQLServer) Lock() error {\n\treturn database.CasRestoreOnErr(&ss.isLocked, false, true, database.ErrLocked, func() error {\n\t\taid, err := database.GenerateAdvisoryLockId(ss.config.DatabaseName, ss.config.SchemaName)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t// This will block until the lock is acquired.\n\t\t// MS Docs: sp_getapplock: https://docs.microsoft.com/en-us/sql/relational-databases/system-stored-procedures/sp-getapplock-transact-sql?view=sql-server-2017\n\t\tquery := `\n\t\tDECLARE @lockResult int;\n\t\tEXEC @lockResult = sp_getapplock @Resource = @p1, @LockMode = 'Exclusive', @LockOwner = 'Session', @LockTimeout = -1;\n\t\tSELECT @lockResult;`","sourceCodeStart":169,"sourceCodeEnd":205,"githubUrl":"https://github.com/golang-migrate/migrate/blob/01a9643f1475e75bb6d6224ddeaf9d8e2434ca8a/database/sqlserver/sqlserver.go#L169-L205","documentation":"Wraps both errors from closing the SQLServer driver's two underlying connections: ss.conn (the raw database connection) and ss.db (the *sql.DB handle). It fires from Close when either Close call fails, so both underlying failures are reported in one message. Investigate the individual conn/db errors for the root cause, such as connection already closed or network failure.","triggerScenarios":"Calling migrate instance Close while the connection is already broken/closed, or the pool close fails due to network issues or driver state errors.","commonSituations":"Deferred Close after a migration failure on a dead connection; shutting down an app while the DB server is unavailable; double-closing via both the driver and sql.DB lifecycle management.","solutions":["Inspect the wrapped conn/db messages to see which close failed; usually the connection was already dead and the error is safe to log and ignore","Ensure Close is called exactly once and after migrations finish, not twice on the same instance","Verify network/DB availability before shutdown-time close; re-run migrations only after reconnecting"],"exampleFix":"// before\nif err := driver.Close(); err != nil { panic(err) }\n// after\nif err := driver.Close(); err != nil {\n    log.Printf(\"migration driver close failed (may be benign on dead conn): %v\", err)\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"if err := driver.Close(); err != nil {\n    var conns []string\n    if errors.As(err, &wrapped) { /* inspect 'conn:' / 'db:' parts */ }\n    log.Printf(\"sqlserver driver close: %v\", err)\n}","preventionTips":["Close the driver exactly once per instance","Avoid Close on connections known to be dead after a migration failure — log instead of aborting shutdown","Check DB/network availability when close errors appear during shutdown"],"tags":["go","sqlserver","database","connection-close","cleanup"],"backgroundTag":"connection-close-failed","analyzedSha":"01a9643f1475e75bb6d6224ddeaf9d8e2434ca8a","analyzedAt":"2026-09-02T19:38:29.671Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T02:17:09.455Z"}