Skip to content

SDK bindings abort instead of raising when the database schema is newer than the process #1444

Description

@Mandalorian-Wang

Database::ensure_schema already produces a proper, recoverable error when the on-disk schema is newer than the process expects. BoxliteRuntime::default_runtime() then converts that error into a panic!, which crosses the Node FFI boundary as a process abort. SDK callers cannot catch it, log it, or degrade — the process dies.

The CLI handles the identical error correctly, which is what makes this look like an oversight rather than a design choice.

The two code paths

src/boxlite/src/db/mod.rs:118 — returns a recoverable error:

Some(v) if v > schema::SCHEMA_VERSION => {
    return Err(BoxliteError::Database(format!(
        "Schema version mismatch: database has v{}, process expects v{}. \
         Upgrade boxlite to a newer version.",
        v, schema::SCHEMA_VERSION
    )));
}

src/boxlite/src/runtime/core.rs:197 — turns it into a panic:

.unwrap_or_else(|e| panic!("Failed to initialize BoxliteRuntime:\n\n{e}"))

Reproduction

Any version skew across SDKs on one machine reaches this. Verified on Darwin arm64 (macOS 26.1).

# 1. Take the shared database to the current schema with a current SDK
pip install -U boxlite                        # 0.10.0
python3 -c "
import asyncio, boxlite
async def m():
    async with boxlite.SimpleBox(image='alpine:latest') as b:
        print(b.id)
asyncio.run(m())"

# 2. Install the newest Node package that can actually load (see #1443)
D=$(mktemp -d) && cd "$D" && npm init -y >/dev/null
npm install @boxlite-ai/boxlite@0.9.7

# 3. Wrap everything in try/catch and run it
cat > t.mjs <<'JS'
import { SimpleBox } from "@boxlite-ai/boxlite";
try {
  const box = new SimpleBox({ image: "alpine:latest" });
  await box.start();
  console.log("started");
} catch (err) {
  console.log("CAUGHT:", err.message);
}
console.log("still alive after try/catch");
JS
node t.mjs

Actual:

thread '<unnamed>' (23402646) panicked at src/boxlite/src/runtime/core.rs:173:37:
Failed to initialize BoxliteRuntime:

storage error: Failed to initialize database at /Users/<user>/.boxlite/db/boxlite.db: database error: Schema version mismatch: database has v10, process expects v8. Upgrade boxlite to a newer version.
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
fatal runtime error: failed to initiate panic, error 5, aborting

Neither CAUGHT: nor still alive after try/catch is ever printed. The process aborts inside box.start().

Contrast: the CLI gets this right

Same machine, same database, same underlying error:

$ boxlite ls
Error: storage error: Failed to initialize database at /Users/<user>/.boxlite/db/boxlite.db: database error: Schema version mismatch: database has v10, process expects v8. Upgrade boxlite to a newer version.

Clean message, normal exit, no panic. The error is recoverable — only the SDK entry point discards that.

Impact

Every SDK example in the documentation teaches try/catch (Python) or try/catch/finally (Node) around box creation. That pattern silently does not apply to this failure. A service that creates boxes cannot log the reason, fall back, or return a 500 — the worker process is gone.

The trigger is narrow (version skew), but the failure mode is the worst available: an uncatchable abort in a library.

Suggested fix

Propagate the Result to the binding layer rather than unwrapping it. core.rs:149 already documents a variant that "returns a Result instead of panicking" — the SDK entry points should be using that path, so BoxliteError::Database arrives in Python as an exception and in Node as a rejected promise.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions