t8y2/dbx · error · DuckDbWorkerError

duckdb_worker_busy

duckdb_worker_busy

Error message

DuckDB worker is already executing a query

What it means

The DuckDB worker's Execute method rejects incoming queries while an interrupt is flagged, i.e. the worker is still executing a previous query. DuckDB operations are not reentrant in this worker, so a second Execute arriving mid-run returns a duckdb_worker_busy error response instead of being processed.

Source

Thrown at agents/drivers/duckdb/src/runtime.rs:288

                let params = request.parse_params()?;
                Ok(DuckDbWorkerResponse::ok(request.id, session.get_table_ddl(params)?))
            }),
            DuckDbWorkerMethod::CompletionAssistant => self.handle_session_request(request, |session, request| {
                let params = request.parse_params()?;
                Ok(DuckDbWorkerResponse::ok(request.id, session.completion_assistant(params)?))
            }),
            DuckDbWorkerMethod::GetObjectSource => self.handle_session_request(request, |session, request| {
                let params = request.parse_params()?;
                Ok(DuckDbWorkerResponse::ok(request.id, session.get_object_source(params)?))
            }),
            DuckDbWorkerMethod::AttachDatabase => self.handle_session_request(request, |session, request| {
                session.attach_database(request.parse_params()?)?;
                Ok(DuckDbWorkerResponse::ok_empty(request.id))
            }),
            DuckDbWorkerMethod::Execute => {
                if self.active_interrupt.lock().unwrap_or_else(|e| e.into_inner()).is_some() {
                    return WorkerHandleResult {
                        response: Some(DuckDbWorkerResponse::err(
                            request.id,
                            DuckDbWorkerError::new("duckdb_worker_busy", "DuckDB worker is already executing a query"),
                        )),
                        shutdown: false,
                    };
                }

                let interrupt_handle = match self.session.lock().unwrap_or_else(|e| e.into_inner()).interrupt_handle() {
                    Ok(handle) => handle,
                    Err(err) => {
                        return WorkerHandleResult {
                            response: Some(DuckDbWorkerResponse::err(
                                request.id,
                                DuckDbWorkerError::from_message("duckdb_not_connected", err),
                            )),
                            shutdown: false,
                        }
                    }

View on GitHub (pinned to c0390bff16)

Solutions

  1. Serialize queries client-side: await the response (or the resolved promise) before sending the next Execute request.
  2. Wait for or clear the active interrupt state before issuing the next query; if a previous cancel wedged, recycle the worker process.
  3. Queue requests in a driver-level mutex/semaphore so concurrency never reaches the worker while busy.

Example fix

// before
void runQuery(sql); runQuery(sql2);
// after
const busy = await isWorkerBusy();
if (busy) await waitUntilIdle();
await runQuery(sql);
await runQuery(sql2);
Defensive patterns

Strategy: retry

Validate before calling

// TS: check busy state before dispatching
async function canExecute(worker: DuckDbWorker): Promise<boolean> {
  return !(await worker.getInterruptFlag());
}

Type guard

function isBusyError(e: unknown): e is { code: "duckdb_worker_busy" } {
  return typeof e === "object" && e !== null && (e as any).code === "duckdb_worker_busy";
}

Try / catch

try {
  await worker.execute(sql);
} catch (e) {
  if (isBusyError(e)) {
    await worker.waitForIdle();
    return worker.execute(sql);
  }
  throw e;
}

Prevention

When it happens

Trigger: Sending a DuckDbWorkerMethod::Execute request (runtime.rs:288) while active_interrupt is Some — a prior query is still running or its interrupt/cancel flag was not cleared, often when the client pipelines queries without awaiting responses.

Common situations: Frontend issuing a new query while a long-running one executes; a cancelled query leaving active_interrupt set; race between interrupt handling and the next request.

Related errors


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