⌘K ctrl+k
Search Shortcut cmd + k | ctrl + k
Troubleshoot

Overview

This page collects common issues encountered when using the DuckDB Go client, together with their workarounds. Because the client uses cgo to embed DuckDB, most build problems are cgo or linker problems. If you run into a problem that is not covered here, search the client's issue tracker on GitHub.

Classifying DuckDB Errors

The client returns a *duckdb.Error for errors reported by DuckDB. Use errors.As to handle these errors by category without parsing their messages:

_, err := db.Exec(query)
if err != nil {
    var duckdbError *duckdb.Error
    if errors.As(err, &duckdbError) {
        switch duckdbError.Type {
        case duckdb.ErrorTypeCatalog:
            // Handle a missing table, view, or other catalog entry.
        case duckdb.ErrorTypeConstraint:
            // Handle a constraint violation.
        }
    }
}

duckdb.Error.Type is a duckdb.ErrorType. Other categories include ErrorTypeBinder, ErrorTypeParser, ErrorTypeConversion, ErrorTypeIO, ErrorTypeOutOfRange, and ErrorTypeInvalidInput. The Msg field contains the error message.

undefined: conn and Other cgo Errors

An undefined: conn error while building means the Go compiler has decided cgo is unavailable, so the DuckDB bindings were not compiled. There are two common causes:

  • Build tools are missing. cgo needs a C compiler and toolchain. On Debian or Ubuntu, install them with:

      sudo apt-get update && sudo apt-get install build-essential
    
  • Cross-compiling disables cgo. The Go compiler turns cgo off automatically when cross-compiling. Re-enable it and point at the right cross-compiler:

      CC=⟨c_cross_compiler⟩ CGO_ENABLED=1 go build
    

Windows Setup

On Windows you need a compatible version of gcc and the necessary runtime libraries. One way is MSYS2: install it, then open an MSYS2 shell and install the UCRT64 toolchain:

pacman -S mingw-w64-ucrt-x86_64-gcc

Add gcc to the path, for example in PowerShell:

$env:PATH = "C:\msys64\ucrt64\bin;$env:PATH"

The package then compiles on Windows.

Linking DuckDB

By default the client statically links a pre-built DuckDB library into the binary, which increases the binary size but requires no separate DuckDB installation. Pre-built libraries ship for macOS (amd64, arm64), Linux (amd64, arm64), and Windows (amd64). If none of the pre-built libraries fit, link a custom library instead.

Linking a Custom Static Library

Build a DuckDB static library from source with DuckDB's bundle-library Makefile target, which produces build/release/libduckdb_bundle.a:

cd /path/to/duckdb
make bundle-library DUCKDB_PLATFORM=any BUILD_EXTENSIONS="icu;json;parquet;autocomplete"

Then build the Go module against that archive with the duckdb_use_static_lib build tag, passing the linker flags for the archive. For example, on macOS ARM64:

CGO_ENABLED=1 \
CPPFLAGS="-DDUCKDB_STATIC_BUILD" \
CGO_LDFLAGS="-lduckdb_bundle -lc++ -L/path/to/libs" \
go build -tags=duckdb_use_static_lib

This is also how a local DuckDB checkout is validated from the Go client, and how the client's own Makefile and CI build. It is required to target platforms without a pre-built library, including FreeBSD, for which DuckDB publishes no bundled library.

Linking a Dynamic Library

Alternatively, link dynamically against a libduckdb shared library on the system with the duckdb_use_lib build tag. Download the shared library (.so on Linux, .dylib on macOS) from the DuckDB releases page, then build and run with the library on the loader path:

# On Linux.
CGO_ENABLED=1 CGO_LDFLAGS="-lduckdb -L/path/to/libs" go build -tags=duckdb_use_lib main.go
LD_LIBRARY_PATH=/path/to/libs ./main

# On macOS.
CGO_ENABLED=1 CGO_LDFLAGS="-lduckdb -L/path/to/libs" go build -tags=duckdb_use_lib main.go
DYLD_LIBRARY_PATH=/path/to/libs ./main

TIMESTAMP versus TIMESTAMP_TZ

In the C API, DuckDB stores both TIMESTAMP and TIMESTAMP_TZ as the same instant, a number of microseconds since 1970-01-01 UTC, without offset information. When a time.Time is passed to the client, it is converted to that instant with UnixMicro(), even for TIMESTAMP_TZ, and scanning either type back returns an instant, because SQL types do not model a per-value time zone.

A bare time.Time binds as TIMESTAMP_TZ by default. When a different timestamp type is required, for example binding against a TIMESTAMP_NS column, force the type with duckdb.Typed():

row := db.QueryRow(query,
    duckdb.Typed(start, duckdb.TYPE_TIMESTAMP_NS),
    duckdb.Typed(end, duckdb.TYPE_TIMESTAMP_NS),
)

See Run Queries for the full example.

Scanning JSON Values

Starting with v2, scanning a JSON value directly into a string or []byte is no longer supported. Scan into any or into the duckdb.Composite wrapper instead, as shown in Run Queries. If a plain string or byte result is needed and the JSON structure is not, cast the column to ::VARCHAR or ::BLOB in SQL before scanning.

Further Reading

  • Go Client — installation, build tags, and the bundled extensions.
  • Connect — opening databases and the connection lifetime, whose failures are often build or linking problems.
  • Run Queries — parameter binding, including the duckdb.Typed() hint and JSON scanning.
© 2026 DuckDB Foundation, Amsterdam NL
DuckDB Home Code of Conduct Trademark Use Blog