Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/nimdeveloper/better-duck/llms.txt

Use this file to discover all available pages before exploring further.

Every fallible operation in better-duck-core returns better_duck_core::error::Result<T>, a type alias for Result<T, Error>. The Error enum covers everything from DuckDB C-API failures and FFI encoding issues to column-index mismatches and async-pool failures. Because Error is #[non_exhaustive], you should always include a wildcard arm when matching — new variants may be added in future releases without a breaking change.
Error is marked #[non_exhaustive]. Always add a _ => { … } arm to match expressions targeting Error to stay forward-compatible as new variants are added.

The Error Enum

The variants below are defined in better_duck_core::error::Error.

DuckDBFailure(FFIError, Option<String>)

Wraps an error code returned by the DuckDB C API (duckdb_state). The second field, when present, contains an additional human-readable message from DuckDB or from the better-duck call site. This is the most common error variant — nearly any SQL execution failure surfaces here.
use better_duck_core::error::Error;

match err {
    Error::DuckDBFailure(ffi_err, Some(msg)) => eprintln!("DuckDB error: {msg}"),
    Error::DuckDBFailure(ffi_err, None)      => eprintln!("DuckDB error code: {ffi_err}"),
    _ => {}
}

IntegralValueOutOfRange(usize, i128)

Returned when an integer value read from DuckDB cannot fit in the target Rust type. The usize is the zero-based column index, and the i128 is the actual value returned by DuckDB. For example, reading a BIGINT column whose value is 1000 into a u8 produces this error.

Utf8Error(str::Utf8Error)

Returned when a byte sequence from DuckDB (such as a VARCHAR column’s raw bytes) cannot be decoded as valid UTF-8. Wraps the standard library’s str::Utf8Error.

NulError(std::ffi::NulError)

Returned when a string passed to better-duck (such as a file path or a parameter name) contains an interior nul byte, which the DuckDB C API cannot represent. Wraps std::ffi::NulError.

InvalidParameterName(String)

Returned when a named parameter (e.g. $my_param) is referenced in a call but no corresponding placeholder exists in the SQL statement. The String contains the unrecognised parameter name.

InvalidPath(PathBuf)

Returned when a filesystem path cannot be converted to a C-compatible string — most commonly because the path contains non-UTF-8 bytes on platforms where Rust paths are not guaranteed to be UTF-8. The PathBuf contains the offending path.

ExecuteReturnedResults

Returned when execute (or a similar write-oriented method) is called on a statement that produces rows. If you need to iterate the result set, use conn.execute(sql) (which returns a DuckResult iterator) instead.

QueryReturnedNoRows

Returned when a query was expected to produce at least one row — for example, when calling a helper that asserts a single-row result — but the result set is empty.

InvalidColumnIndex(usize)

Returned when a column is accessed by a zero-based index that is out of range for the current row. The usize is the invalid index.

InvalidColumnName(String)

Returned when a column is accessed by name (e.g. row.get("label")) but no column with that name exists in the result set. The String contains the unrecognised name.

StatementChangedRows(usize)

Returned when a DML statement affected an unexpected number of rows. The usize is the actual count of rows changed. This is typically used by helpers that assert exactly one row was modified.

ToSqlConversionFailure(Box<dyn Error + Send + Sync + 'static>)

Returned when the AppendAble trait implementation for a user type fails to convert its value for DuckDB binding. The inner Box<dyn Error + Send + Sync + 'static> contains the source error from the AppendAble implementor.

InvalidQuery

Returned when a read-only query path (such as CachedStatement) is handed a statement that is not a SELECT — i.e. a statement that would modify the database.

MultipleStatement

Returned when a method that expects a single SQL statement receives a string containing more than one statement (;-separated). Use execute_batch for multi-statement strings.

InvalidParameterCount(usize, usize)

Returned when the number of parameters bound to a statement does not match the number of $N placeholders in the SQL. The first usize is the number of parameters provided; the second is the number expected.

AppendError

Returned when the DuckDB bulk-appender API reports a failure during Appender::append or Appender::save. No additional detail is available beyond the variant itself; check that the schema of the values you are appending matches the target table.

ConversionError(DuckDBConversionError)

Returned when a value read from DuckDB cannot be converted to the expected Rust type at the value level (as opposed to DuckDBFailure, which is a lower-level C-API error). See the DuckDBConversionError section below for sub-variant details.

BackgroundTaskFailed(String)

Returned by the async feature when a tokio::task::spawn_blocking task panicked or was cancelled before it could produce a result. The String contains a diagnostic message. This variant only appears when using AsyncConnection, AsyncDatabase, or AsyncPool.

Pool(String)

Returned by the pool feature when an r2d2 connection pool operation fails — for example, a checkout timeout or a connection manager error. The String contains the pool’s error message.

UNKNOWN(Box<dyn Error + Send + Sync + 'static>)

A catch-all variant for errors that do not fit any more specific category. The inner Box<dyn Error + Send + Sync + 'static> contains the original source. Prefer the specific variants above when constructing errors in your own code.

DuckDBConversionError

ConversionError(DuckDBConversionError) wraps this sub-enum, which describes exactly how a column value could not be converted:
VariantDescription
TypeMismatch { expected: duckdb_type, found: duckdb_type }The DuckDB column’s physical type did not match the Rust type that was requested. Both expected and found are duckdb_type FFI constants.
ConversionError(String)A general conversion failure described by a string message — used when a more precise variant does not apply.
NullValueA NULL was encountered in a column or field that was expected to be non-null.
PrecisionLoss(String)The conversion would lose precision — for example, a DECIMAL value whose scale exceeds what the target type can represent. The String contains a description of the overflow.

Matching Errors

use better_duck_core::error::Error;

fn handle(err: Error) {
    match err {
        Error::DuckDBFailure(_, Some(ref msg)) => eprintln!("DuckDB said: {msg}"),
        Error::QueryReturnedNoRows             => eprintln!("no rows returned"),
        Error::InvalidColumnName(ref name)     => eprintln!("unknown column: {name}"),
        Error::ConversionError(ref cv)         => eprintln!("conversion failed: {cv:?}"),
        Error::BackgroundTaskFailed(ref msg)   => eprintln!("async task failed: {msg}"),
        // Error is #[non_exhaustive] — always include a wildcard arm
        other => eprintln!("other error: {other}"),
    }
}

Using ? Propagation

better_duck_core::error::Result<T> is a type alias for Result<T, Error>. Any function that returns Result<T, better_duck_core::error::Error> (or the alias directly) can use ? to propagate errors automatically:
use better_duck_core::{connection::Connection, error::Result};

fn run_query() -> Result<()> {
    let mut conn = Connection::open_in_memory()?;

    conn.execute_batch(
        "CREATE TABLE logs (id INTEGER, msg TEXT);
         INSERT INTO logs VALUES (1, 'hello');",
    )?;

    let mut result = conn.execute("SELECT id, msg FROM logs")?;
    for row in result {
        let row = row?;
        println!("{:?}", row.get("msg"));
    }
    Ok(())
}
If your function needs to return errors from both better-duck and other crates, wrap Error in your own error type (e.g. using thiserror) and implement From<better_duck_core::error::Error> for it, which lets ? convert automatically.

Build docs developers (and LLMs) love