t8y2/dbx · error

Backup destination is invalid

Error message

Backup destination is invalid

What it means

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.

Source

Thrown at crates/dbx-sqlite-worker/src/runtime.rs:200

                    let row_size = serde_json::to_vec(&values).map(|encoded| encoded.len()).unwrap_or(0);
                    if !rows.is_empty() && encoded_bytes + row_size > MAX_RESPONSE_JSON_BYTES {
                        truncated = true;
                        break;
                    }
                    encoded_bytes += row_size;
                    rows.push(values);
                },
                Err(error) => return WorkerBody::err(error.to_string()),
            }
            WorkerBody::query(columns, column_types, rows, 0, truncated)
        }
        Err(error) => WorkerBody::err(error.to_string()),
    }
}

fn backup(conn: &Connection, dest: &str) -> WorkerBody {
    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");
    }

View on GitHub (pinned to c0390bff16)

Solutions

  1. Provide a concrete, non-empty destination path for the backup
  2. Strip NUL bytes when converting paths from raw bytes/C strings
  3. Validate the destination on the caller side before sending the Backup op
  4. Ensure the destination directory exists or is creatable (parent dirs are auto-created on success paths)

Example fix

// before
let dest = opts.dest.unwrap_or_default(); // ""
send(Backup { dest });
// after
let dest = opts.dest.ok_or("backup destination required")?;
assert!(!dest.trim().is_empty() && !dest.contains('\0'));
send(Backup { dest });
Defensive patterns

Strategy: validation

Validate before calling

fn validate_backup_dest(dest: &str) -> Result<&str, &'static str> {
    if dest.trim().is_empty() { return Err("backup destination is required"); }
    if dest.contains('\0') { return Err("backup destination contains NUL byte"); }
    Ok(dest)
}

Type guard

fn is_safe_path(p: &str) -> bool { !p.trim().is_empty() && !p.contains('\0') }

Try / catch

match worker.send(WorkerOp::Backup { dest: dest.to_string() }).await {
    Err(body) if body.error() == Some("Backup destination is invalid") => {
        eprintln!("Provide a non-empty destination path without NUL bytes");
    }
    other => other?,
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of t8y2/dbx@c0390bff16 (2026-09-05). Data as JSON: /api/errors/a4d7bb6809b0fdc4. Report an issue: GitHub.