{"record":{"id":"d9607448a02d58bd","repo":"tinyhumansai/openhuman","slug":"smithery-get-qualified-name-returned-http-statu","errorCode":null,"errorMessage":"Smithery GET {qualified_name} returned HTTP {status}: {}","messagePattern":"Smithery GET (.+?) returned HTTP (.+?): (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/openhuman/mcp/registry/registries/smithery.rs","lineNumber":129,"sourceCode":"\n        tracing::debug!(\"[smithery] get fetching qualified_name={qualified_name}\");\n\n        let client = http_client()?;\n        let url = format!(\n            \"{SMITHERY_BASE}/servers/{}\",\n            urlencoding_encode(qualified_name)\n        );\n        let req = apply_auth(\n            config,\n            client.get(&url).header(\"Accept\", \"application/json\"),\n        );\n\n        let resp = req.send().await.context(\"Smithery get request failed\")?;\n        let status = resp.status();\n        let body = resp.text().await.context(\"Smithery get read failed\")?;\n\n        if !status.is_success() {\n            anyhow::bail!(\n                \"Smithery GET {qualified_name} returned HTTP {status}: {}\",\n                &body[..body.len().min(200)]\n            );\n        }\n\n        let mut detail: SmitheryServerDetail = serde_json::from_str(&body)\n            .with_context(|| format!(\"Failed to parse Smithery detail: {body}\"))?;\n        detail.source = SOURCE_SMITHERY.to_string();\n\n        let _ = store::set_cached(config, &cache_key, &body);\n        tracing::debug!(\n            \"[smithery] get ok qualified_name={qualified_name} connections={}\",\n            detail.connections.len()\n        );\n\n        Ok(detail)\n    }\n}","sourceCodeStart":111,"sourceCodeEnd":147,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/749120085864ce16e0f273c7b86fac7740b39c5b/src/openhuman/mcp/registry/registries/smithery.rs#L111-L147","documentation":"Raised when fetching a single MCP server's detail — get(config, qualified_name) with the name URL-encoded — and Smithery answers non-2xx. The dominant real case is 404: the qualified name (e.g. io.github.example/server) no longer exists upstream because the package was renamed, removed, or its version qualifier went away. Auth, rate-limit, and outage statuses surface here exactly as they do in search; a successful result is cached under 'smithery:detail:<qualified_name>'.","triggerScenarios":"Calling SmitheryRegistry::get with a qualified_name captured from stale metadata — a previously cached detail, a saved install record, or a hardcoded name — after the upstream package was deleted or renamed; also 401/403/429/5xx under the same conditions as the search endpoint.","commonSituations":"Reinstalling or updating a server whose upstream repo was renamed; saved install records referencing a version-pinned qualified name after the publisher cut a new release; registry outages; corporate proxies.","solutions":["If 404: re-run a catalog search for the server's short name and use the qualified name it returns now — the old one is gone upstream.","If the server vanished upstream, uninstall the stale local entry (store::delete_server) so UI lists stop referencing it.","For 401/403/429/5xx, apply the same fixes as for search errors: credentials, backoff, proxy allowlisting.","If a renamed package is served from cache, note the detail cache key format (smithery:detail:<qualified_name>) when investigating staleness."],"exampleFix":"// before\nlet detail = registry.get(config, &qualified_name).await?;\n\n// after — on 404, re-resolve the current qualified name via search, then retry\nlet detail = match registry.get(config, &qualified_name).await {\n    Ok(d) => d,\n    Err(e) if e.to_string().contains(\"HTTP 404\") => {\n        let results = registry.search(config, &short_name, 1, 20).await?;\n        let fresh = results.servers.iter()\n            .find(|s| s.qualified_name.contains(&short_name))\n            .ok_or_else(|| anyhow::anyhow!(\"'{qualified_name}' no longer exists in the Smithery catalog\"))?;\n        registry.get(config, &fresh.qualified_name).await?\n    }\n    Err(e) => return Err(e),\n};","handlingStrategy":"validation","validationCode":"// Resolve the current qualified name from a fresh search before the detail GET\nlet results = registry.search(config, &short_name, 1, 20).await?;\nlet listed = results.servers.iter().map(|s| s.qualified_name.clone()).collect::<Vec<_>>();\nif !listed.contains(&qualified_name) {\n    anyhow::bail!(\"stale qualified name {qualified_name}; catalog now lists {listed:?}\");\n}","typeGuard":null,"tryCatchPattern":"Match err.to_string() for `HTTP 404` and treat it as 'package removed/renamed' — offer a re-search or uninstall instead of a generic failure. Other statuses follow the search-error triage: auth vs transient vs network.","preventionTips":["Store the short name alongside the qualified name so a re-search is always possible.","Refresh catalog metadata before reinstall/update flows.","Treat cached details older than the cache TTL as suspect after upstream renames."],"tags":["mcp","smithery","http","registry","not-found"],"backgroundTag":"upstream-api-http-error","analyzedSha":"749120085864ce16e0f273c7b86fac7740b39c5b","analyzedAt":"2026-08-17T21:21:45.363Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}