How to Connect to Snowflake from Go

9 min readSnowflake

Snowflake has exactly one Go driver: the official gosnowflake from Snowflake itself. It implements the standard database/sql interface, so you connect, query, and scan rows the same way you would against any other SQL database in Go. The parts that trip people up are Snowflake-specific: the account identifier format, the move to v2 of the driver in March 2026, and the fact that Snowflake now blocks single-factor password auth for new accounts, which pushes you toward key-pair (JWT) authentication.

The options

PackageImport pathInterfaceVersion (as of June 2026)
gosnowflake v2github.com/snowflakedb/gosnowflake/v2database/sqlv2.1.0
gosnowflake v1github.com/snowflakedb/gosnowflakedatabase/sqlv1.19.1

There is no third-party alternative worth considering. Snowflake speaks its own protocol over HTTPS, not the Postgres or MySQL wire protocol, so the official driver is the only one that understands it.

v1 vs v2: Version 2.0.0 was released on March 3rd, 2026 and is a major version with breaking changes. v1 (currently v1.19.1) is still maintained and receives fixes, so existing code does not have to migrate immediately. New projects should start on v2 because that is where active development lives. The two cannot be imported under the same name in the same module, so pick one. The v2 changes that matter to most code:

  • Import path gains /v2 (github.com/snowflakedb/gosnowflake/v2).
  • Arrow batches moved to a separate sub-package, gosnowflake/v2/arrowbatches, so you only pull the Arrow dependency if you use it.
  • Config fields renamed: KeepSessionAlive is now ServerSessionKeepAlive, and InsecureMode is replaced by DisableOCSPChecks. DisableTelemetry was removed (use the CLIENT_TELEMETRY_ENABLED session parameter).
  • The skip-registration env var typo was fixed: GOSNOWFLAKE_SKIP_REGISTERATION became GOSNOWFLAKE_SKIP_REGISTRATION.

The rest of this guide uses v2.

Install

go get github.com/snowflakedb/gosnowflake/v2

The account identifier trap

Snowflake's biggest connection gotcha is the account identifier. The current recommended format is <organization>-<account_name> with a hyphen, not a dot:

myorg-myaccount

You will also see the legacy format that includes a region and cloud, like xy12345.eu-central-1.aws. Both still work, but the org-account form is what Snowflake recommends today. The value goes after the @ in the DSN. A frequent mistake is pasting the full account URL (myorg-myaccount.snowflakecomputing.com) as the identifier; use just the identifier part, or pass the full hostname explicitly via the Config.Host field.

You can find your account identifier in the Snowsight UI under your account menu, or by running SELECT CURRENT_ORGANIZATION_NAME(), CURRENT_ACCOUNT_NAME();.

Connect with a DSN

The driver registers itself under the name snowflake. The DSN format is:

username[:password]@<account_identifier>/dbname/schemaname?warehouse=wh&role=myrole
package main
 
import (
	"database/sql"
	"log"
 
	_ "github.com/snowflakedb/gosnowflake/v2"
)
 
func main() {
	dsn := "jsmith:mypassword@myorg-myaccount/mydb/public?warehouse=compute_wh&role=analyst"
 
	db, err := sql.Open("snowflake", dsn)
	if err != nil {
		log.Fatal(err)
	}
	defer db.Close()
 
	if err := db.Ping(); err != nil {
		log.Fatal(err)
	}
 
	var version string
	if err := db.QueryRow("SELECT CURRENT_VERSION()").Scan(&version); err != nil {
		log.Fatal(err)
	}
	log.Printf("connected to Snowflake %s", version)
}

Two things mirror every other database/sql driver and are worth repeating:

  • sql.Open does not open a connection. It validates the DSN and returns a handle. The first real network round-trip happens on Ping or the first query, which is why the Ping above is how you actually confirm the credentials and warehouse are valid.
  • The blank import (_ "...gosnowflake/v2") is what registers the snowflake driver name. Forget it and sql.Open returns sql: unknown driver "snowflake".

Building the DSN safely

Passwords and identifiers with special characters need escaping. Rather than concatenating strings, build a Config and let the driver produce a correct DSN:

import sf "github.com/snowflakedb/gosnowflake/v2"
 
cfg := &sf.Config{
	Account:   "myorg-myaccount",
	User:      "jsmith",
	Password:  "mypassword",
	Database:  "mydb",
	Schema:    "public",
	Warehouse: "compute_wh",
	Role:      "analyst",
}
 
dsn, err := sf.DSN(cfg)
if err != nil {
	log.Fatal(err)
}
db, err := sql.Open("snowflake", dsn)

Key-pair (JWT) authentication

As of late 2025, Snowflake blocks new connections that use a password as the only factor. For programmatic access (which is what a Go service is), the recommended path is key-pair authentication. You generate an RSA key pair, register the public key on the Snowflake user, and the driver signs a JWT with the private key.

Generate the key pair:

# 2048-bit PKCS8 private key
openssl genpkey -algorithm RSA \
  -pkeyopt rsa_keygen_bits:2048 \
  -pkeyopt rsa_keygen_pubexp:65537 | \
  openssl pkcs8 -topk8 -outform der -nocrypt > rsa_key.p8
 
# matching public key
openssl pkey -pubout -inform der -outform der \
  -in rsa_key.p8 -out rsa_key.pub

Register the public key on the user (base64 of the DER public key, no header lines):

ALTER USER jsmith SET RSA_PUBLIC_KEY='MIIBIjANBgkq...';

Then load and parse the private key in Go and pass it via Config:

import (
	"crypto/rsa"
	"crypto/x509"
	"database/sql"
	"log"
	"os"
 
	sf "github.com/snowflakedb/gosnowflake/v2"
)
 
func connect() (*sql.DB, error) {
	der, err := os.ReadFile("rsa_key.p8")
	if err != nil {
		return nil, err
	}
	parsed, err := x509.ParsePKCS8PrivateKey(der)
	if err != nil {
		return nil, err
	}
	privKey := parsed.(*rsa.PrivateKey)
 
	cfg := &sf.Config{
		Account:       "myorg-myaccount",
		User:          "jsmith",
		Authenticator: sf.AuthTypeJwt,
		PrivateKey:    privKey,
		Database:      "mydb",
		Schema:        "public",
		Warehouse:     "compute_wh",
	}
	dsn, err := sf.DSN(cfg)
	if err != nil {
		return nil, err
	}
	return sql.Open("snowflake", dsn)
}

Note that Go's standard library does not support passphrase-encrypted PKCS8 keys, so generate the key with -nocrypt as above, or decrypt it in your application before parsing. The JWT is recreated on each retry and is short-lived, so you do not manage token expiry yourself.

For SSO in interactive contexts, set Authenticator: sf.AuthTypeExternalBrowser, but that is for human logins, not a server.

Parameter binding

Snowflake uses positional ? placeholders with database/sql:

rows, err := db.Query(
	"SELECT id, email FROM users WHERE country = ? AND created_at > ?",
	"CH", "2026-01-01",
)
if err != nil {
	log.Fatal(err)
}
defer rows.Close()
 
for rows.Next() {
	var id int64
	var email string
	if err := rows.Scan(&id, &email); err != nil {
		log.Fatal(err)
	}
	// use id, email
}
if err := rows.Err(); err != nil { // catches errors that ended iteration
	log.Fatal(err)
}

Always defer rows.Close() and check rows.Err() after the loop. A for rows.Next() loop can stop because the data ended or because an error occurred mid-stream, and only rows.Err() distinguishes the two.

Identifiers come back uppercase

Snowflake folds unquoted identifiers to uppercase. A column written as select email comes back as EMAIL in result metadata, and a table created as users is stored as USERS. This matters when you read column names dynamically or build a tool that lists tables. If you created an object with quotes to force mixed case (CREATE TABLE "myTable"), you must quote it everywhere, including inside the DSN, which means escaping the inner quotes. Avoid mixed-case identifiers unless you have a reason.

The "no active warehouse" error

000606: No active warehouse selected in the current session.

This is not a connection failure. It means you connected but never selected a warehouse, so Snowflake has nowhere to run the query. Set warehouse= in the DSN or Warehouse in the Config, and make sure the role you connect with has USAGE on that warehouse. Unlike a Postgres or MySQL server, Snowflake separates storage from compute, and a query needs a running warehouse to execute.

Connection pooling

*sql.DB is a pool, and it is safe for concurrent use, so create one and share it across goroutines. Snowflake connections are HTTPS sessions rather than long-lived TCP connections to a single server, so the tuning advice differs slightly from a traditional database:

db.SetMaxOpenConns(10)
db.SetMaxIdleConns(5)
db.SetConnMaxLifetime(0) // sessions are managed server-side; no hard need to recycle

Each open connection corresponds to a Snowflake session, and sessions have their own timeout managed by the server. If your workload is bursty, keep MaxIdleConns low so you are not holding idle sessions. For long-running services that keep connections parked, set ServerSessionKeepAlive: true in the Config to stop the server from expiring idle sessions out from under you.

Context and cancellation

Use the context-aware methods so a cancelled request or a timeout actually cancels the running query in Snowflake, rather than leaking a warehouse-consuming query:

ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
 
rows, err := db.QueryContext(ctx, "SELECT count(*) FROM large_table")

Because Snowflake bills for warehouse time, cancelling abandoned queries is not just hygiene, it has a direct cost impact.

Common errors

ErrorLikely cause
sql: unknown driver "snowflake"Missing the blank import of the driver package
260008: account is emptyAccount identifier missing or malformed in the DSN
390100: Incorrect username or passwordWrong credentials, or password auth blocked (switch to key-pair)
JWT token is invalidPublic key not registered, key mismatch, or clock skew on the client
000606: No active warehouse selectedNo warehouse set, or role lacks USAGE on it
390201: cannot perform operation; no database selecteddatabase not set and queries use unqualified names

Where Mako fits

Once you can connect, you still need to explore the data: list tables, check column types, write and iterate on queries. Mako connects to Snowflake (and PostgreSQL, MySQL, ClickHouse, BigQuery, and more) with AI-powered autocomplete, so you can prototype the exact queries your Go service will run before you hardcode them. It is a read and query tool, not a schema-management console, so you will still use Snowsight or SQL for CREATE/ALTER work.

For loading data into Snowflake first, see our import CSV to Snowflake and import JSON to Snowflake guides. To connect from other languages, see connect to Snowflake from Python and connect to Snowflake from Node.js.

Mako connects to Snowflake with AI-powered autocomplete. Try it free at mako.ai.

Mako — open source

Skip the terminal. Use Mako.

Connect your database, write queries with AI assistance, and import/export data in clicks. Free to start.