Introduction

The sqlx library extends Go's database/sql with named query support, allowing struct fields to be used as query parameters. When struct tags do not match query parameter names, pointer fields are nil, or IN clauses are not handled correctly, sqlx returns missing destination name, could not find name errors, or silently binds wrong values. The named query system uses reflection to map struct fields to SQL parameters, which means tag mismatches and type incompatibilities are only detected at runtime.

Symptoms

bash
missing destination name status in *models.User

Or:

bash
could not find name created_at in User

Or silent wrong values:

go
// Query uses :user_id but struct has field ID with tag `db:"id"`
// Parameter not bound, query uses NULL

Common Causes

  • Struct tag does not match query param: db:"user_id" in struct but :userId in query
  • Unexported struct field: sqlx cannot access lowercase fields
  • Nil pointer field: Pointer field is nil, sqlx cannot determine type for binding
  • IN clause not handled: :ids with slice requires special sqlx.In handling
  • Embedding conflict: Embedded struct has conflicting db tag
  • Time zone not set: time.Time serialized with wrong timezone

Step-by-Step Fix

Step 1: Match struct tags to query parameters

go type User struct { ID int64 db:"id" Name string db:"name" Email string db:"email" Status string db:"status" CreatedAt time.Time db:"created_at"` }

// Query parameter names MUST match db tags query := SELECT id, name, email, status, created_at FROM users WHERE status = :status

result, err := db.NamedQuery(query, map[string]interface{}{ "status": "active", }) ```

Step 2: Handle nil pointer fields

go type NullableUser struct { ID int64 db:"id" Name *string db:"name" // Can be nil Email string db:"email" Age *int db:"age" // Can be nil UpdatedAt *time.Time db:"updated_at"` // Can be nil }

// Insert with nullable fields user := NullableUser{ Name: nil, // Will bind as NULL Email: "test@example.com", Age: nil, }

query := INSERT INTO users (name, email, age) VALUES (:name, :email, :age) _, err := db.NamedExec(query, user) // sqlx correctly handles nil pointers as SQL NULL ```

Step 3: Handle IN clauses with sqlx.In

```go import "github.com/jmoiron/sqlx"

func getUsersByIDs(db *sqlx.DB, ids []int64) ([]User, error) { query, args, err := sqlx.In( SELECT id, name, email FROM users WHERE id IN (?), ids, ) if err != nil { return nil, err }

// For NamedQuery with IN, use regular Queryx query = db.Rebind(query) // Convert ? to database-specific placeholders rows, err := db.Queryx(query, args...) if err != nil { return nil, err } defer rows.Close()

var users []User for rows.Next() { var u User if err := rows.StructScan(&u); err != nil { return nil, err } users = append(users, u) } return users, nil } ```

Prevention

  • Keep struct db:"..." tags in sync with SQL column names
  • Use pointer types for fields that can be NULL in the database
  • Use sqlx.In for IN clauses -- named queries do not handle slices automatically
  • Use db.Rebind() to convert ? placeholders to database-specific format
  • Enable sqlx debug logging to see actual query and parameter values
  • Add struct tag validation in tests using reflection
  • Use NamedExec with map[string]interface{} for dynamic query building