{"record":{"id":"ebd106c468760ae4","repo":"RightNow-AI/openfang","slug":"e","errorCode":null,"errorMessage":"{e}","messagePattern":"\\{e\\}","errorType":"http","errorClass":null,"httpStatus":404,"severity":"error","filePath":"crates/openfang-api/src/routes.rs","lineNumber":9015,"sourceCode":"            \"connected\": connected > 0,\n            \"message\": format!(\"Integration '{}' installed\", id),\n        })),\n    )\n}\n\n/// DELETE /api/integrations/:id — Remove an integration.\npub async fn remove_integration(\n    State(state): State<Arc<AppState>>,\n    Path(id): Path<String>,\n) -> impl IntoResponse {\n    // Scope the write lock\n    let uninstall_err = {\n        let mut registry = state\n            .kernel\n            .extension_registry\n            .write()\n            .unwrap_or_else(|e| e.into_inner());\n        registry.uninstall(&id).err()\n    };\n\n    if let Some(e) = uninstall_err {\n        return (\n            StatusCode::NOT_FOUND,\n            Json(serde_json::json!({\"error\": e.to_string()})),\n        );\n    }\n\n    state.kernel.extension_health.unregister(&id);\n\n    // Hot-disconnect the removed MCP server\n    let _ = state.kernel.reload_extension_mcps().await;\n\n    (\n        StatusCode::OK,\n        Json(serde_json::json!({\n            \"id\": id,","sourceCodeStart":8997,"sourceCodeEnd":9033,"githubUrl":"https://github.com/RightNow-AI/openfang/blob/acf2587e46be174c10200489c9a2d23a39a98aeb/crates/openfang-api/src/routes.rs#L8997-L9033","documentation":"The extension uninstall HTTP route acquires the extension_registry write lock (tolerating poisoning via into_inner) and calls registry.uninstall(&id). When uninstall returns an Err, the route responds 404 NOT_FOUND with the error serialized as {\"error\": ...} — meaning the registry could not uninstall that extension id.","triggerScenarios":"DELETE/POST to the uninstall endpoint with an extension id that is not currently installed, or an id the registry refuses to uninstall (unknown id producing an internal error).","commonSituations":"Extension already uninstalled in a prior request (double-delete); client using a stale id after a reinstall changed ids; typo'd extension id in the request path/body.","solutions":["Confirm the extension id exists via the registry list endpoint before uninstalling.","Make the client treat 404 as idempotent success for repeat uninstall calls.","Read the JSON error body — it carries the registry's specific reason (e.g. 'extension not found').","If ids change on reinstall, look up the current id by extension name instead of caching it."],"exampleFix":"// before\nlet res = client.delete(&format!(\"/extensions/{id}/uninstall\")).send().await?;\nres.error_for_status()?;\n// after\nlet res = client.delete(&format!(\"/extensions/{id}/uninstall\")).send().await?;\nmatch res.status() {\n    StatusCode::OK => Ok(()),\n    StatusCode::NOT_FOUND => Ok(()), // already uninstalled — idempotent\n    _ => Err(anyhow!(res.text().await?)),\n}","handlingStrategy":"try-catch","validationCode":"// Client-side: confirm the extension exists before uninstalling\nlet list = client.get(\"/extensions\").send().await?.json::<Vec<Extension>>().await?;\nif !list.iter().any(|x| x.id == id) {\n    return Ok(()); // nothing to uninstall — skip\n}","typeGuard":"fn is_uninstall_not_found(status: StatusCode, body: &serde_json::Value) -> bool {\n    status == StatusCode::NOT_FOUND && body[\"error\"].is_string()\n}","tryCatchPattern":"// Rust (HTTP client)\nlet res = client.delete(&format!(\"/extensions/{id}/uninstall\")).send().await?;\nmatch res.status() {\n    StatusCode::OK => Ok(()),\n    StatusCode::NOT_FOUND => Ok(()), // treat as idempotent success\n    _ => Err(anyhow!(\"uninstall failed: {}\", res.text().await?)),\n}","preventionTips":["Make uninstall calls idempotent — ignore 404 on repeat deletes.","List extensions and resolve the id by name instead of caching ids.","Avoid double-submitting the uninstall request from UI code.","Read the JSON error body for the registry's specific reason."],"tags":["http-404","extension-registry","uninstall"],"backgroundTag":"resource-not-found","analyzedSha":"acf2587e46be174c10200489c9a2d23a39a98aeb","analyzedAt":"2026-09-02T22:42:28.464Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T02:17:09.455Z"}