SQL Rows Not Closed — Connection Leak

sql: database/sql: Rows.Scan called without calling Rows.Close (or connection pool exhausted)

Quick Answer

Always close sql.Rows with defer rows.Close() after checking the error. Not closing rows leaks database connections.

Why This Happens

When you call db.Query, Go borrows a connection from the pool and ties it to the Rows object. The connection is returned to the pool only when Rows.Close() is called. If you forget to close Rows, the connection leaks and eventually the pool is exhausted, blocking all queries.

The Problem

func getUsers(db *sql.DB) ([]string, error) {
    rows, err := db.Query("SELECT name FROM users")
    if err != nil {
        return nil, err
    }
    // rows never closed!
    var names []string
    for rows.Next() {
        var name string
        rows.Scan(&name)
        names = append(names, name)
    }
    return names, nil
}

The Fix

func getUsers(db *sql.DB) ([]string, error) {
    rows, err := db.Query("SELECT name FROM users")
    if err != nil {
        return nil, err
    }
    defer rows.Close()
    var names []string
    for rows.Next() {
        var name string
        if err := rows.Scan(&name); err != nil {
            return nil, err
        }
        names = append(names, name)
    }
    return names, rows.Err()
}

Step-by-Step Fix

  1. 1

    Identify unclosed rows

    Find db.Query calls where rows.Close() is not called or not deferred.

  2. 2

    Add defer rows.Close()

    Add defer rows.Close() immediately after the error check on db.Query.

  3. 3

    Check rows.Err()

    Always check rows.Err() after the iteration loop to catch errors that occurred during iteration.

Bugsly catches this automatically

Bugsly's AI analyzes this error pattern in real-time, explains what went wrong in plain English, and suggests the exact fix — before your users even report it.

Try Bugsly free