{"record":{"id":"8bd099137b0b3e39","repo":"tinyhumansai/openhuman","slug":"failed-to-add-what","errorCode":null,"errorMessage":"Failed to add {what}","messagePattern":"Failed to add (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"src/openhuman/mcp/registry/store.rs","lineNumber":140,"sourceCode":"/// common case, but that check-then-alter is not atomic *across connections*:\n/// every store call opens its own [`Connection`] and runs `init_schema`, so the\n/// several MCP RPCs a single page load fans out (list / status / registry) can\n/// each snapshot the column as missing before any of them adds it — then all\n/// race to `ALTER`, and every loser fails with \"duplicate column name\". SQLite's\n/// `ADD COLUMN` has no `IF NOT EXISTS`, so we swallow exactly that error: the\n/// column existing is the desired post-condition, and surfacing it turned a\n/// benign race into the red \"Failed to add deployment_url column to mcp_servers\"\n/// banner on the MCP Servers page (#4194). Any other failure still propagates.\nfn add_column_idempotent(conn: &Connection, ddl: &str, what: &str) -> Result<()> {\n    match conn.execute(ddl, []) {\n        Ok(_) => Ok(()),\n        Err(rusqlite::Error::SqliteFailure(_, Some(msg)))\n            if msg.contains(\"duplicate column name\") =>\n        {\n            log::debug!(\"[mcp_registry] {what} already present (concurrent migration) — skipping\");\n            Ok(())\n        }\n        Err(e) => Err(anyhow::Error::new(e).context(format!(\"Failed to add {what}\"))),\n    }\n}\n\n/// Snapshot of the column names on `mcp_servers`. Used by the additive\n/// migration in [`init_schema`] to decide which `ALTER TABLE ADD COLUMN`\n/// statements still need to run on this DB.\nfn mcp_servers_columns(conn: &Connection) -> Result<Vec<String>> {\n    let mut stmt = conn\n        .prepare(\"PRAGMA table_info(mcp_servers)\")\n        .context(\"prepare PRAGMA table_info\")?;\n    // PRAGMA table_info row shape: (cid, name, type, notnull, dflt_value, pk).\n    let mut rows = stmt.query([])?;\n    let mut cols = Vec::new();\n    while let Some(row) = rows.next()? {\n        let name: String = row.get(1)?;\n        cols.push(name);\n    }\n    Ok(cols)","sourceCodeStart":122,"sourceCodeEnd":158,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/749120085864ce16e0f273c7b86fac7740b39c5b/src/openhuman/mcp/registry/store.rs#L122-L158","documentation":"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).","triggerScenarios":"Thrown at src/openhuman/mcp/registry/store.rs:140 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Inspect the underlying SQLite error logged alongside to distinguish lock/IO from schema incompatibility","If the DB is locked by concurrent init, serialize store access or enable busy_timeout/WAL","Restore or recreate the mcp registry database if its schema is corrupted beyond the migration's reach"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"749120085864ce16e0f273c7b86fac7740b39c5b","analyzedAt":"2026-08-17T21:21:45.363Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}