t8y2/dbx · error
Restore source is invalid
Error message
Restore source is invalid
What it means
The restore() helper validates the source backup path: it must be non-empty after trimming and contain no NUL bytes. Invalid paths are rejected up front with this error instead of letting SQLite fail with a confusing message.
Source
Thrown at crates/dbx-sqlite-worker/src/runtime.rs:217
if dest.trim().is_empty() || dest.contains('\0') {
return WorkerBody::err("Backup destination is invalid");
}
if let Some(parent) = Path::new(dest).parent() {
if !parent.as_os_str().is_empty() {
if let Err(error) = std::fs::create_dir_all(parent) {
return WorkerBody::err(error.to_string());
}
}
}
match conn.backup(DatabaseName::Main, dest, None) {
Ok(()) => WorkerBody::ok(),
Err(error) => WorkerBody::err(format!("SQLite backup failed: {error}")),
}
}
fn restore(conn: &mut Connection, src: &str) -> WorkerBody {
if src.trim().is_empty() || src.contains('\0') {
return WorkerBody::err("Restore source is invalid");
}
match conn.restore(DatabaseName::Main, src, None::<fn(_)>) {
Ok(()) => WorkerBody::ok(),
Err(error) => WorkerBody::err(format!("SQLite restore failed: {error}")),
}
}
fn value_to_json(value: rusqlite::types::ValueRef<'_>, column_decl_type: Option<&str>) -> (serde_json::Value, bool) {
match value {
rusqlite::types::ValueRef::Null => (json!(null), false),
rusqlite::types::ValueRef::Integer(value) => (json!(value), false),
rusqlite::types::ValueRef::Real(value) => (json!(value), false),
rusqlite::types::ValueRef::Text(value) => (json!(String::from_utf8_lossy(value)), false),
rusqlite::types::ValueRef::Blob(value) => sqlite_blob_value_to_json(value, column_decl_type),
}
}
fn sqlite_blob_value_to_json(bytes: &[u8], column_decl_type: Option<&str>) -> (serde_json::Value, bool) {View on GitHub (pinned to c0390bff16)
Solutions
- Supply a valid, non-empty path to an existing backup file
- Strip NUL bytes from paths derived from raw bytes
- Validate the source path (exists, readable) before sending the Restore op
- Check where the path is sourced from (arg/config/env) and add default/required checks
Example fix
// before
let src = cfg.backup_path.unwrap_or_default(); // ""
send(Restore { src });
// after
let src = cfg.backup_path.context("backup source required")?;
assert!(!src.trim().is_empty() && !src.contains('\0'));
send(Restore { src }); Defensive patterns
Strategy: validation
Validate before calling
fn validate_restore_src(src: &str) -> Result<&str, &'static str> {
if src.trim().is_empty() { return Err("restore source is required"); }
if src.contains('\0') { return Err("restore source contains NUL byte"); }
let p = std::path::Path::new(src);
if !p.is_file() { return Err("restore source does not exist"); }
Ok(src)
} Type guard
fn is_restorable(p: &str) -> bool {
!p.trim().is_empty() && !p.contains('\0') && std::path::Path::new(p).is_file()
} Try / catch
match worker.send(WorkerOp::Restore { src: src.to_string() }).await {
Err(body) if body.error() == Some("Restore source is invalid") => {
eprintln!("Provide an existing backup file path without NUL bytes");
}
other => other?,
} Prevention
- Require the backup path argument; fail fast when unset
- Check the file exists and is readable before restore
- Strip NULs when converting paths from raw bytes
- Record backup paths in a manifest so restore scripts never guess
When it happens
Trigger: WorkerOp::Restore with src = "", whitespace-only, or containing '\0'; a restore job whose source path was never populated; paths converted from C strings retaining a NUL terminator.
Common situations: Missing backup-path argument in a restore script; config key not set; path built from user input that was sanitized to empty; buffer-to-path conversions including NUL.
Related errors
- Backup destination is invalid
- SQL is empty
- Agent runtime thread limits must be positive
- Databend procedure names with special characters are not sup
- Unsupported object type: null
AI-assisted analysis of t8y2/dbx@c0390bff16 (2026-09-05).
Data as JSON: /api/errors/a906514c3025b556.
Report an issue: GitHub.