tinyhumansai/openhuman · error · anyhow::Error

Failed to add {what}

Error message

Failed to add {what}

What it means

A column-add migration on the MCP registry SQLite store failed with something other than the deliberately-swallowed 'duplicate column name' race. Because each store call opens its own connection and runs init_schema, concurrent RPCs race to ALTER TABLE; SQLite ADD COLUMN has no IF NOT EXISTS, so the duplicate-column loser is expected and swallowed. This message means a different ALTER failure — the index/migration is genuinely broken (locked DB, disk error, incompatible schema).

Source

Thrown at src/openhuman/mcp/registry/store.rs:140

/// common case, but that check-then-alter is not atomic *across connections*:
/// every store call opens its own [`Connection`] and runs `init_schema`, so the
/// several MCP RPCs a single page load fans out (list / status / registry) can
/// each snapshot the column as missing before any of them adds it — then all
/// race to `ALTER`, and every loser fails with "duplicate column name". SQLite's
/// `ADD COLUMN` has no `IF NOT EXISTS`, so we swallow exactly that error: the
/// column existing is the desired post-condition, and surfacing it turned a
/// benign race into the red "Failed to add deployment_url column to mcp_servers"
/// banner on the MCP Servers page (#4194). Any other failure still propagates.
fn add_column_idempotent(conn: &Connection, ddl: &str, what: &str) -> Result<()> {
    match conn.execute(ddl, []) {
        Ok(_) => Ok(()),
        Err(rusqlite::Error::SqliteFailure(_, Some(msg)))
            if msg.contains("duplicate column name") =>
        {
            log::debug!("[mcp_registry] {what} already present (concurrent migration) — skipping");
            Ok(())
        }
        Err(e) => Err(anyhow::Error::new(e).context(format!("Failed to add {what}"))),
    }
}

/// Snapshot of the column names on `mcp_servers`. Used by the additive
/// migration in [`init_schema`] to decide which `ALTER TABLE ADD COLUMN`
/// statements still need to run on this DB.
fn mcp_servers_columns(conn: &Connection) -> Result<Vec<String>> {
    let mut stmt = conn
        .prepare("PRAGMA table_info(mcp_servers)")
        .context("prepare PRAGMA table_info")?;
    // PRAGMA table_info row shape: (cid, name, type, notnull, dflt_value, pk).
    let mut rows = stmt.query([])?;
    let mut cols = Vec::new();
    while let Some(row) = rows.next()? {
        let name: String = row.get(1)?;
        cols.push(name);
    }
    Ok(cols)

View on GitHub (pinned to 7491200858)

Solutions

  1. Inspect the underlying SQLite error logged alongside to distinguish lock/IO from schema incompatibility
  2. If the DB is locked by concurrent init, serialize store access or enable busy_timeout/WAL
  3. Restore or recreate the mcp registry database if its schema is corrupted beyond the migration's reach
Defensive patterns

Strategy: try-catch

When it happens

Trigger: Thrown at src/openhuman/mcp/registry/store.rs:140 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of tinyhumansai/openhuman@7491200858 (2026-08-17). Data as JSON: /api/errors/8bd099137b0b3e39. Report an issue: GitHub.