{"record":{"id":"94fcfa1cda0ac772","repo":"cjpais/Handy","slug":"failed-to-init-hugging-face-api","errorCode":null,"errorMessage":"Failed to init Hugging Face API: {}","messagePattern":"Failed to init Hugging Face API: (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"src-tauri/src/managers/model.rs","lineNumber":1977,"sourceCode":"            let stream_count = ATTEMPT_STREAMS[attempt - 1];\n            info!(\n                \"HF download attempt {}/{} for {} using {} concurrent stream(s)\",\n                attempt,\n                ATTEMPT_STREAMS.len(),\n                model_id,\n                stream_count\n            );\n\n            // Fresh client per attempt so a wedged connection from the previous\n            // try can't poison the retry.\n            let api = ApiBuilder::from_env()\n                // Ignore cached and environment-provided credentials. A stale token\n                // can make otherwise-public downloads fail authentication.\n                .with_token(None)\n                .with_progress(false)\n                .with_max_files(stream_count)\n                .build()\n                .map_err(|e| anyhow::anyhow!(\"Failed to init Hugging Face API: {}\", e))?;\n            let repo = api.repo(Repo::with_revision(\n                repo_id.clone(),\n                RepoType::Model,\n                revision.clone(),\n            ));\n            let progress = HfDownloadProgress::new(self.app_handle.clone(), model_id.clone());\n\n            // hf-hub has no internal timeouts, so a wedged connection would\n            // otherwise hang this attempt forever and neither the retry loop\n            // nor the mirror fallback would ever fire. The watchdog cancels a\n            // per-attempt child token when progress goes stale; a user cancel\n            // on the parent propagates through the same child.\n            let attempt_token = cancel_token.child_token();\n            let watchdog = tokio::spawn({\n                let probe = progress.clone();\n                let attempt_token = attempt_token.clone();\n                async move {\n                    loop {","sourceCodeStart":1959,"sourceCodeEnd":1995,"githubUrl":"https://github.com/cjpais/Handy/blob/ba10ce1943ef34e93c09494027fc0b9ced2e8a44/src-tauri/src/managers/model.rs#L1959-L1995","documentation":"hf-hub's `ApiBuilder::from_env().build()` returned Err before any network request was made. `build()` validates configuration derived from the environment (endpoint URL from HF_ENDPOINT, cache location from HF_HOME/HF_HUB_CACHE, proxy settings) and constructs the HTTP client; a malformed endpoint URL, an uncreatable/unwritable cache directory, or an invalid proxy env var makes it fail. Because the code explicitly strips credentials with `.with_token(None)`, authentication is not the cause — this is a config/environment problem.","triggerScenarios":"HF_ENDPOINT set to a malformed or non-URL value (e.g. a typo'd mirror like \"hf-mirror.com\" without scheme); HF_HOME or HF_HUB_CACHE pointing to a path the process cannot create or write; an invalid HTTP_PROXY/HTTPS_PROXY value; sandboxed environment where the metadata cache path cannot be determined.","commonSituations":"Users redirecting HF traffic to a mirror (hf-mirror.com) with a typo'd env var; corporate proxy env vars set incorrectly; app run from a sandbox/flatpak without write access to the default cache dir.","solutions":["Unset or fix HF_ENDPOINT (must be a full valid URL) and retry the download","Check HF_HOME / HF_HUB_CACHE point to a writable directory the app can create","Fix or remove invalid HTTP_PROXY / HTTPS_PROXY / ALL_PROXY env vars","If config is clean and it persists, capture the inner error text after 'Failed to init Hugging Face API:' — it names the exact field that failed validation"],"exampleFix":"// before\nlet api = ApiBuilder::from_env().with_token(None).build()\n    .map_err(|e| anyhow::anyhow!(\"Failed to init Hugging Face API: {}\", e))?;\n\n// after — fail fast on the most common config culprit with an actionable message\nif let Ok(ep) = std::env::var(\"HF_ENDPOINT\") {\n    if !ep.starts_with(\"http://\") && !ep.starts_with(\"https://\") {\n        anyhow::bail!(\"HF_ENDPOINT must be a full URL including scheme, got: {ep}\");\n    }\n}\nlet api = ApiBuilder::from_env().with_token(None).build()\n    .map_err(|e| anyhow::anyhow!(\"Failed to init Hugging Face API: {}\", e))?;","handlingStrategy":"validation","validationCode":"// preflight the environment before entering the download retry loop\nfn hf_config_sane() -> Result<()> {\n    if let Ok(ep) = std::env::var(\"HF_ENDPOINT\") {\n        if !ep.starts_with(\"http://\") && !ep.starts_with(\"https://\") {\n            anyhow::bail!(\"HF_ENDPOINT must be a full URL, got: {ep}\");\n        }\n    }\n    let cache = hf_hub::Cache::default().path().parent().unwrap().to_path_buf();\n    std::fs::create_dir_all(&cache)\n        .with_context(|| format!(\"HF cache dir not writable: {cache:?}\"))?;\n    Ok(())\n}","typeGuard":null,"tryCatchPattern":"// in the caller: distinguish config errors (do not retry) from transfer errors (retry/mirror)\nlet err = self.download_hf_model(&info, repo, rev).await.unwrap_err();\nif err.to_string().contains(\"Failed to init Hugging Face API\") {\n    // config problem — surface an actionable message, skip mirror fallback\n    return Err(err.context(\"Check HF_ENDPOINT / HF_HOME / proxy env vars\"));\n}","preventionTips":["Validate HF_ENDPOINT/HF_HOME/HF_HUB_CACHE at download start, not after build() fails","Keep per-attempt fresh ApiBuilder (already done) so one bad config attempt can't poison retries","Log the full inner error from ApiBuilder::build — it names the failing config field"],"tags":["rust","huggingface","hf-hub","environment-variables","download"],"backgroundTag":"api-client-init-failed","analyzedSha":"ba10ce1943ef34e93c09494027fc0b9ced2e8a44","analyzedAt":"2026-08-17T10:29:55.597Z","contentChangedAt":"2026-08-17T10:29:55.597Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}