{"record":{"id":"a906514c3025b556","repo":"t8y2/dbx","slug":"restore-source-is-invalid","errorCode":null,"errorMessage":"Restore source is invalid","messagePattern":"Restore source is invalid","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/dbx-sqlite-worker/src/runtime.rs","lineNumber":217,"sourceCode":"    if dest.trim().is_empty() || dest.contains('\\0') {\n        return WorkerBody::err(\"Backup destination is invalid\");\n    }\n    if let Some(parent) = Path::new(dest).parent() {\n        if !parent.as_os_str().is_empty() {\n            if let Err(error) = std::fs::create_dir_all(parent) {\n                return WorkerBody::err(error.to_string());\n            }\n        }\n    }\n    match conn.backup(DatabaseName::Main, dest, None) {\n        Ok(()) => WorkerBody::ok(),\n        Err(error) => WorkerBody::err(format!(\"SQLite backup failed: {error}\")),\n    }\n}\n\nfn restore(conn: &mut Connection, src: &str) -> WorkerBody {\n    if src.trim().is_empty() || src.contains('\\0') {\n        return WorkerBody::err(\"Restore source is invalid\");\n    }\n    match conn.restore(DatabaseName::Main, src, None::<fn(_)>) {\n        Ok(()) => WorkerBody::ok(),\n        Err(error) => WorkerBody::err(format!(\"SQLite restore failed: {error}\")),\n    }\n}\n\nfn value_to_json(value: rusqlite::types::ValueRef<'_>, column_decl_type: Option<&str>) -> (serde_json::Value, bool) {\n    match value {\n        rusqlite::types::ValueRef::Null => (json!(null), false),\n        rusqlite::types::ValueRef::Integer(value) => (json!(value), false),\n        rusqlite::types::ValueRef::Real(value) => (json!(value), false),\n        rusqlite::types::ValueRef::Text(value) => (json!(String::from_utf8_lossy(value)), false),\n        rusqlite::types::ValueRef::Blob(value) => sqlite_blob_value_to_json(value, column_decl_type),\n    }\n}\n\nfn sqlite_blob_value_to_json(bytes: &[u8], column_decl_type: Option<&str>) -> (serde_json::Value, bool) {","sourceCodeStart":199,"sourceCodeEnd":235,"githubUrl":"https://github.com/t8y2/dbx/blob/c0390bff16418b651f4728520d99adf8ce48829a/crates/dbx-sqlite-worker/src/runtime.rs#L199-L235","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nlet src = cfg.backup_path.unwrap_or_default(); // \"\"\nsend(Restore { src });\n// after\nlet src = cfg.backup_path.context(\"backup source required\")?;\nassert!(!src.trim().is_empty() && !src.contains('\\0'));\nsend(Restore { src });","handlingStrategy":"validation","validationCode":"fn validate_restore_src(src: &str) -> Result<&str, &'static str> {\n    if src.trim().is_empty() { return Err(\"restore source is required\"); }\n    if src.contains('\\0') { return Err(\"restore source contains NUL byte\"); }\n    let p = std::path::Path::new(src);\n    if !p.is_file() { return Err(\"restore source does not exist\"); }\n    Ok(src)\n}","typeGuard":"fn is_restorable(p: &str) -> bool {\n    !p.trim().is_empty() && !p.contains('\\0') && std::path::Path::new(p).is_file()\n}","tryCatchPattern":"match worker.send(WorkerOp::Restore { src: src.to_string() }).await {\n    Err(body) if body.error() == Some(\"Restore source is invalid\") => {\n        eprintln!(\"Provide an existing backup file path without NUL bytes\");\n    }\n    other => other?,\n}","preventionTips":["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"],"tags":["sqlite","restore","validation","path"],"backgroundTag":"invalid-path","analyzedSha":"c0390bff16418b651f4728520d99adf8ce48829a","analyzedAt":"2026-09-05T23:05:10.900Z","contentChangedAt":"2026-09-05T23:05:10.900Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}