t8y2/dbx · error

SQLite worker has no open database

Error message

SQLite worker has no open database

What it means

The SQLite worker's Query operation was received while no database connection is open. The worker protocol requires an Open (or successful open path) before Query; otherwise it replies with this error body instead of panicking on a null connection.

Source

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

        if matches!(response.body, WorkerBody::Ok { .. }) {
            // keep serving
        }
    }
    Ok(())
}

fn handle(connection: &mut Option<Connection>, request: WorkerRequest) -> WorkerResponse {
    let body = match request.op {
        WorkerOp::Open { path } => match open_database(&path) {
            Ok(opened) => {
                *connection = Some(opened);
                WorkerBody::ok()
            }
            Err(error) => WorkerBody::err(error),
        },
        WorkerOp::Query { sql, max_rows } => match connection.as_mut() {
            Some(conn) => query(conn, &sql, max_rows.unwrap_or(10_000)),
            None => WorkerBody::err("SQLite worker has no open database"),
        },
        WorkerOp::Backup { dest } => match connection.as_mut() {
            Some(conn) => backup(conn, &dest),
            None => WorkerBody::err("SQLite worker has no open database"),
        },
        WorkerOp::Restore { src } => match connection.as_mut() {
            Some(conn) => restore(conn, &src),
            None => WorkerBody::err("SQLite worker has no open database"),
        },
        WorkerOp::Ping => WorkerBody::pong(),
        WorkerOp::Close => {
            *connection = None;
            WorkerBody::ok()
        }
    };
    WorkerResponse { id: request.id, body }
}

View on GitHub (pinned to c0390bff16)

Solutions

  1. Send WorkerOp::Open with a valid database path before any Query
  2. Check the result of the Open op and handle its error before proceeding
  3. If a Close was issued, reopen before issuing further ops
  4. Pool workers so each op sequence starts with a successful open

Example fix

// before
send(Query { sql }); // no prior open
// after
send(Open { path: "app.db" });
send(Query { sql });
Defensive patterns

Strategy: type-guard

Validate before calling

fn require_open(worker: &WorkerHandle) -> Result<(), &'static str> {
    if worker.last_op_was_successful_open() { Ok(()) } else { Err("worker has no open database") }
}

Type guard

type OpenWorker = WorkerHandle;
fn as_open(worker: &WorkerHandle) -> Option<&OpenWorker> {
    worker.is_open().then_some(worker)
}

Try / catch

match worker.send(WorkerOp::Query { sql, max_rows: None }).await {
    Err(body) if body.error() == Some("SQLite worker has no open database") => {
        worker.send(WorkerOp::Open { path }).await?;
        worker.send(WorkerOp::Query { sql, max_rows: None }).await?
    }
    other => other?,
}

Prevention

When it happens

Trigger: Sending WorkerOp::Query before WorkerOp::Open; after a Close operation; after a failed/aborted open; reusing a worker whose connection was never established because an earlier open errored.

Common situations: Client code that skips the open step assuming auto-connect; connection lost/closed by an earlier failed operation; test harnesses exercising ops without setup.

Related errors


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