transact-rs/sqlx · error · io::Error (InvalidData)

filename passed to SQLite must be valid UTF-8

Error message

filename passed to SQLite must be valid UTF-8

What it means

EstablishParams::from_options converts the SqliteConnectOptions filename (a PathBuf) into a String to build the connection URL for SQLite. Because the SQLite C API path here requires UTF-8, sqlx refuses non-UTF-8 paths with this InvalidData error rather than passing lossy bytes to SQLite. It surfaces during SqlitePool::connect/connect_with when the database path is not valid UTF-8.

Source

Thrown at sqlx-sqlite/src/connection/establish.rs:46

    open_flags: i32,
    busy_timeout: Duration,
    statement_cache_capacity: usize,
    log_settings: LogSettings,
    #[cfg(feature = "load-extension")]
    extensions: IndexMap<CString, Option<CString>>,
    pub(crate) thread_name: String,
    pub(crate) command_channel_size: usize,
    #[cfg(feature = "regexp")]
    register_regexp_function: bool,
}

impl EstablishParams {
    pub fn from_options(options: &SqliteConnectOptions) -> Result<Self, Error> {
        let mut filename = options
            .filename
            .to_str()
            .ok_or_else(|| {
                io::Error::new(
                    io::ErrorKind::InvalidData,
                    "filename passed to SQLite must be valid UTF-8",
                )
            })?
            .to_owned();

        // Set common flags we expect to have in sqlite
        let mut flags = SQLITE_OPEN_URI;

        // By default, we connect to an in-memory database.
        // [SQLITE_OPEN_NOMUTEX] will instruct [sqlite3_open_v2] to return an error if it
        // cannot satisfy our wish for a thread-safe, lock-free connection object

        flags |= if options.serialized {
            SQLITE_OPEN_FULLMUTEX
        } else {
            SQLITE_OPEN_NOMUTEX
        };

View on GitHub (pinned to 03af8bcc57)

Solutions

  1. Validate the path with path.to_str() before connecting and fail with a clear user-facing message
  2. Rename/move the database file to a UTF-8-safe path
  3. Normalize the input: convert the OsStr via to_string_lossy only if lossy replacement is acceptable, otherwise reject
  4. Set the process locale/environment so paths read from the OS are UTF-8

Example fix

// before
let opts = SqliteConnectOptions::new().filename(&user_path);
let pool = SqlitePool::connect_with(opts).await?;
// after: check up front
let filename = user_path.to_str().ok_or_else(|| anyhow!("SQLite path {:?} is not valid UTF-8", user_path))?;
let pool = SqlitePool::connect(&format!("sqlite://{filename}")).await?;
Defensive patterns

Strategy: validation

Validate before calling

use std::path::Path;
fn ensure_utf8_path(p: &Path) -> Result<&str, String> {
    p.to_str().ok_or_else(|| format!("SQLite database path {:?} is not valid UTF-8", p))
}

Type guard

fn is_utf8_path(p: &std::path::Path) -> bool {
    p.to_str().is_some()
}

Try / catch

match SqlitePool::connect_with(opts).await {
    Ok(pool) => Ok(pool),
    Err(e) if e.to_string().contains("must be valid UTF-8") => {
        Err(anyhow!("database path is not valid UTF-8; please move/rename the file"))
    }
    Err(e) => Err(e.into()),
}

Prevention

When it happens

Trigger: Calling SqlitePool::connect / SqliteConnectOptions::new().filename(path) where the PathBuf comes from the filesystem on a platform with non-UTF-8 encoding (e.g. Latin-1 filenames on Unix) or contains invalid byte sequences.

Common situations: Deriving the DB path from environment variables or argv on non-UTF-8 locales; files in directories with legacy-encoded names; path constructed from raw OS bytes via OsStr::from_bytes.

Related errors


AI-assisted analysis of transact-rs/sqlx@03af8bcc57 (2026-09-03). Data as JSON: /api/errors/0e1f57965c8f9a86. Report an issue: GitHub.