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
- Validate the path with path.to_str() before connecting and fail with a clear user-facing message
- Rename/move the database file to a UTF-8-safe path
- Normalize the input: convert the OsStr via to_string_lossy only if lossy replacement is acceptable, otherwise reject
- 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
- Validate paths with Path::to_str() before constructing SqliteConnectOptions
- Avoid deriving DB paths from raw OS bytes or legacy-locale environment variables
- Keep database filenames ASCII/UTF-8-safe in deployment configs
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
- query file path cannot be represented as a string
- filename passed to SQLite must not contain nul bytes
- invalid column index: {}
- extension entrypoint names passed to SQLite must not contain
- extension names passed to SQLite must not contain nul bytes
AI-assisted analysis of transact-rs/sqlx@03af8bcc57 (2026-09-03).
Data as JSON: /api/errors/0e1f57965c8f9a86.
Report an issue: GitHub.