transact-rs/sqlx · error · io::Error (InvalidData)
filename passed to SQLite must not contain nul bytes
Error message
filename passed to SQLite must not contain nul bytes
What it means
After appending query parameters, from_options converts the filename string into a CString for the SQLite C API. CString::new fails if the string embeds an interior NUL byte, so sqlx maps that failure to this InvalidData error. An embedded nul in the filename would truncate the path at the C boundary, so sqlx refuses it.
Source
Thrown at sqlx-sqlite/src/connection/establish.rs:108
if let Some(vfs) = options.vfs.as_deref() {
query_params.insert("vfs", vfs);
}
if !query_params.is_empty() {
filename = format!(
"file:{}?",
percent_encoding::percent_encode(filename.as_bytes(), NON_ALPHANUMERIC),
);
// Suffix serializer automatically handles `&` separators for us.
let filename_len = filename.len();
filename = form_urlencoded::Serializer::for_suffix(filename, filename_len)
.extend_pairs(query_params)
.finish();
}
let filename = CString::new(filename).map_err(|_| {
io::Error::new(
io::ErrorKind::InvalidData,
"filename passed to SQLite must not contain nul bytes",
)
})?;
#[cfg(feature = "load-extension")]
let extensions = options
.extensions
.iter()
.map(|(name, entry)| {
let entry = entry
.as_ref()
.map(|e| {
CString::new(e.as_bytes()).map_err(|_| {
io::Error::new(
io::ErrorKind::InvalidData,
"extension entrypoint names passed to SQLite must not contain nul bytes"
)View on GitHub (pinned to 03af8bcc57)
Solutions
- Sanitize/validate the filename before building options: reject or strip any '\0' characters
- Trim the value at the first NUL if the trailing content is known garbage (filename.split('\0').next())
- Fix the source of the path (config file, env var, DB row) that embedded the nul byte
- Add an input-validation error upstream so users get a clearer message than the connection failure
Example fix
// before
let opts = SqliteConnectOptions::new().filename(raw_name); // raw_name may contain '\0'
let pool = SqlitePool::connect_with(opts).await?;
// after
let clean = raw_name.split('\0').next().context("filename contained nul byte")?;
let pool = SqlitePool::connect(&format!("sqlite://{clean}")).await?; Defensive patterns
Strategy: validation
Validate before calling
fn ensure_no_nul(s: &str) -> Result<&str, String> {
if s.contains('\0') {
Err(format!("SQLite filename contains nul byte: {:?}", s))
} else {
Ok(s)
}
}
let filename = ensure_no_nul(&raw_name)?;
let opts = SqliteConnectOptions::new().filename(filename); Type guard
fn is_nul_free(s: &str) -> bool {
!s.as_bytes().contains(&b'\0')
} Try / catch
match SqlitePool::connect_with(opts).await {
Ok(pool) => Ok(pool),
Err(e) if e.to_string().contains("must not contain nul bytes") => {
Err(anyhow!("database path contains a nul byte; check the configuration source"))
}
Err(e) => Err(e.into()),
} Prevention
- Reject '\0' in any filename or option value sourced from user or binary input
- Truncate at first NUL only when trailing data is known garbage
- Sanitize config values read from binary formats, logs, or databases before connecting
When it happens
Trigger: Connecting to SQLite with a SqliteConnectOptions whose filename (or a query-param value appended to it, e.g. mode/cache options derived from user input) contains a '\0' character, producing CString::new failure.
Common situations: Paths or options read from binary input, log files, or user forms containing literal NUL characters; truncated C-string data copied into a Rust String; malicious or corrupt configuration values.
Related errors
- filename passed to SQLite must be valid UTF-8
- extension entrypoint names passed to SQLite must not contain
- extension names passed to SQLite must not contain nul bytes
- invalid column index: {}
- SQLite is unable to allocate memory to hold the sqlite3 obje
AI-assisted analysis of transact-rs/sqlx@03af8bcc57 (2026-09-03).
Data as JSON: /api/errors/edfcd16971ea44ac.
Report an issue: GitHub.