tursodatabase/turso · error

Error setting Ctrl-C handler

Error message

Error setting Ctrl-C handler

What it means

On startup the tursopg CLI registers a Ctrl-C handler with ctrlc::set_handler that increments an AtomicUsize interrupt counter (postgres/cli/main.rs:1007-1011) and unwraps the result with expect("Error setting Ctrl-C handler"). ctrlc::set_handler fails when another SIGINT/SIGTERM handler is already installed in the process (the crate permits exactly one) or when the platform's signal mechanism cannot be initialized - and the expect turns that into a process abort.

Source

Thrown at postgres/cli/main.rs:1021

fn main() -> anyhow::Result<()> {
    let opts = Opts::parse();
    let _guard = init_tracing(&opts)?;

    let db_file = opts
        .database
        .as_ref()
        .map_or(":memory:".to_string(), |p| p.to_string_lossy().to_string());

    let (io, conn) = open_database(&db_file, opts.vfs.as_ref(), opts.readonly)?;

    let interrupt_count = Arc::new(AtomicUsize::new(0));
    {
        let ic = Arc::clone(&interrupt_count);
        ctrlc::set_handler(move || {
            ic.fetch_add(1, Ordering::Release);
        })
        .expect("Error setting Ctrl-C handler");
    }

    auto_attach_pg_schemas(&conn, &db_file);
    // Server mode: start PG wire protocol server and exit
    if let Some(ref address) = opts.server {
        let server = TursoPgServer::new(address.clone(), db_file, conn, interrupt_count);
        return server.run();
    }

    let table_config = TableConfig::adaptive_colors();

    // Execute a single SQL command and exit
    if let Some(ref sql) = opts.sql {
        let had_error = execute_sql(&conn, sql, &table_config, false, &mut std::io::stdout());
        conn.close()?;
        if had_error {
            std::process::exit(1);
        }

View on GitHub (pinned to c1e5928725)

Solutions

  1. Run tursopg as its own process (subprocess) instead of embedding it in a host that owns signal handling.
  2. If you control the host, register exactly one Ctrl-C handler for the whole process and fan the event out to tursopg and other consumers yourself.
  3. Remove or defer competing signal registrations before tursopg starts.
  4. Report an issue requesting graceful degradation: log-and-continue without Ctrl-C support instead of panicking.

Example fix

// before: two registrations in one process - second one panics
ctrlc::set_handler(|| println!("host"));
run_tursopg_cli(args); // internally calls ctrlc::set_handler again

// after: one handler, fan out manually
ctrlc::set_handler(|| {
    println!("host");
    interrupt_tursopg(); // route the signal to the CLI yourself
});
Defensive patterns

Strategy: try-catch

Try / catch

If embedding: never rely on the CLI's own registration; own the single handler yourself and forward interrupts. Rust hosts can also std::panic::catch_unwind(AssertUnwindSafe(|| run_cli(args))) to convert the startup panic into an error and re-run tursopg as a subprocess instead.

Prevention

When it happens

Trigger: Embedding or invoking the tursopg CLI code inside a process that already registered signal handling - a prior ctrlc::set_handler call, tokio::signal, JVM/Python interpreter handlers - or a second registration attempt in the same process; also signal-restricted environments where handler installation fails.

Common situations: Test harnesses that install their own interrupt handling before driving the CLI, host applications embedding tursopg as a library, running under certain debuggers or sanitizers that interfere with signal setup.

Related errors


AI-assisted analysis of tursodatabase/turso@c1e5928725 (2026-08-20). Data as JSON: /api/errors/2ef305d3e5aef436. Report an issue: GitHub.