{"record":{"id":"a4d7bb6809b0fdc4","repo":"t8y2/dbx","slug":"backup-destination-is-invalid","errorCode":null,"errorMessage":"Backup destination is invalid","messagePattern":"Backup destination is invalid","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/dbx-sqlite-worker/src/runtime.rs","lineNumber":200,"sourceCode":"                    let row_size = serde_json::to_vec(&values).map(|encoded| encoded.len()).unwrap_or(0);\n                    if !rows.is_empty() && encoded_bytes + row_size > MAX_RESPONSE_JSON_BYTES {\n                        truncated = true;\n                        break;\n                    }\n                    encoded_bytes += row_size;\n                    rows.push(values);\n                },\n                Err(error) => return WorkerBody::err(error.to_string()),\n            }\n            WorkerBody::query(columns, column_types, rows, 0, truncated)\n        }\n        Err(error) => WorkerBody::err(error.to_string()),\n    }\n}\n\nfn backup(conn: &Connection, dest: &str) -> WorkerBody {\n    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    }","sourceCodeStart":182,"sourceCodeEnd":218,"githubUrl":"https://github.com/t8y2/dbx/blob/c0390bff16418b651f4728520d99adf8ce48829a/crates/dbx-sqlite-worker/src/runtime.rs#L182-L218","documentation":"The backup() helper validates the destination path: it must be non-empty after trimming and contain no NUL bytes. Anything else returns this error before touching the filesystem, since rusqlite would fail opaquely on such paths.","triggerScenarios":"WorkerOp::Backup with dest = \"\", whitespace-only, or a string containing '\\0'; a config/argument that never supplied the destination; joining paths programmatically that embedded a NUL.","commonSituations":"Missing CLI arg or unset env var for the backup path; trimming logic that emptied the value; paths built from byte buffers with trailing NULs.","solutions":["Provide a concrete, non-empty destination path for the backup","Strip NUL bytes when converting paths from raw bytes/C strings","Validate the destination on the caller side before sending the Backup op","Ensure the destination directory exists or is creatable (parent dirs are auto-created on success paths)"],"exampleFix":"// before\nlet dest = opts.dest.unwrap_or_default(); // \"\"\nsend(Backup { dest });\n// after\nlet dest = opts.dest.ok_or(\"backup destination required\")?;\nassert!(!dest.trim().is_empty() && !dest.contains('\\0'));\nsend(Backup { dest });","handlingStrategy":"validation","validationCode":"fn validate_backup_dest(dest: &str) -> Result<&str, &'static str> {\n    if dest.trim().is_empty() { return Err(\"backup destination is required\"); }\n    if dest.contains('\\0') { return Err(\"backup destination contains NUL byte\"); }\n    Ok(dest)\n}","typeGuard":"fn is_safe_path(p: &str) -> bool { !p.trim().is_empty() && !p.contains('\\0') }","tryCatchPattern":"match worker.send(WorkerOp::Backup { dest: dest.to_string() }).await {\n    Err(body) if body.error() == Some(\"Backup destination is invalid\") => {\n        eprintln!(\"Provide a non-empty destination path without NUL bytes\");\n    }\n    other => other?,\n}","preventionTips":["Make the backup path a required argument (no defaults to empty string)","Sanitize paths built from raw bytes to strip NULs","Validate path client-side before dispatching ops","Test backup commands with missing-path scenarios"],"tags":["sqlite","backup","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-14T05:17:10.506Z"}