tursodatabase/turso · error · napi::Error

{message}: {e}

Error message

{message}: {e}

What it means

Generic napi error formatter in the JS binding: to_error(status, message, e) renders any Rust std::error::Error as "{message}: {e}" under a caller-chosen napi status. Unlike to_generic_error it is used where the call site picks a specific status; the message prefix still identifies which binding operation failed.

Source

Thrown at bindings/javascript/src/lib.rs:233

            // make the JS step loop spin without letting the backoff expire.
            let sleep_ms = duration.as_millis().clamp(1, u32::MAX as u128) as u32;
            Ok((STEP_SLEEP, sleep_ms))
        }
        Ok(turso_core::StepResult::Done) => Ok((STEP_DONE, 0)),
        Ok(turso_core::StepResult::Interrupt) => {
            Err(create_generic_error("statement was interrupted"))
        }
        Ok(turso_core::StepResult::Busy) => Err(create_generic_error("database is locked")),
        Err(e) => Err(to_generic_error("step failed", e)),
    }
}

fn to_generic_error<E: std::error::Error>(message: &str, e: E) -> napi::Error {
    Error::new(Status::GenericFailure, format!("{message}: {e}"))
}

fn to_error<E: std::error::Error>(status: napi::Status, message: &str, e: E) -> napi::Error {
    Error::new(status, format!("{message}: {e}"))
}

fn create_generic_error(message: &str) -> napi::Error {
    Error::new(Status::GenericFailure, message)
}

fn create_error(status: napi::Status, message: &str) -> napi::Error {
    Error::new(status, message)
}

fn query_timeout_duration(timeout_ms: u32) -> Option<std::time::Duration> {
    if timeout_ms > 0 {
        Some(std::time::Duration::from_millis(timeout_ms as u64))
    } else {
        None
    }
}

View on GitHub (pinned to 244cde92a7)

Solutions

  1. Identify the failing operation from the prefix before the first colon, then treat the suffix as the authoritative native error string
  2. Check the JS arguments passed to that exact API (types, required options)
  3. For open/prepare failures, validate the database path and SQL text before calling
  4. Upgrade the JS and native halves of the binding together
Defensive patterns

Strategy: try-catch

Type guard

function isBindingWrapperError(e: unknown): e is Error {
  return e instanceof Error && e.message.includes(': ');
}

Try / catch

try { db.open(); } catch (e) { const msg = String((e as Error).message); const op = msg.slice(0, msg.indexOf(': ')); const cause = msg.slice(msg.indexOf(': ') + 2); console.error(`${op} failed: ${cause}`); }

Prevention

When it happens

Trigger: Any napi entry point that maps a native error with a non-generic status - argument conversion or open/prepare failures inside the JS wrapper - producing messages like "<operation>: <Rust error string>".

Common situations: Passing wrong argument types or option shapes from JS, opening a database at an unusable path, or wrapper/engine version drift; the operation named before the colon is the API that failed.

Related errors


AI-assisted analysis of tursodatabase/turso@244cde92a7 (2026-08-20). Data as JSON: /api/errors/39a9955da4c1b10b. Report an issue: GitHub.