cjpais/Handy · error · anyhow::Error

Failed to init Hugging Face API

Error message

Failed to init Hugging Face API: {}

What it means

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.

Solutions

  1. Unset or fix HF_ENDPOINT (must be a full valid URL) and retry the download
  2. Check HF_HOME / HF_HUB_CACHE point to a writable directory the app can create
  3. Fix or remove invalid HTTP_PROXY / HTTPS_PROXY / ALL_PROXY env vars
  4. 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

Example fix

// before
let api = ApiBuilder::from_env().with_token(None).build()
    .map_err(|e| anyhow::anyhow!("Failed to init Hugging Face API: {}", e))?;

// after — fail fast on the most common config culprit with an actionable message
if let Ok(ep) = std::env::var("HF_ENDPOINT") {
    if !ep.starts_with("http://") && !ep.starts_with("https://") {
        anyhow::bail!("HF_ENDPOINT must be a full URL including scheme, got: {ep}");
    }
}
let api = ApiBuilder::from_env().with_token(None).build()
    .map_err(|e| anyhow::anyhow!("Failed to init Hugging Face API: {}", e))?;
Defensive patterns

Strategy: validation

Validate before calling

// preflight the environment before entering the download retry loop
fn hf_config_sane() -> Result<()> {
    if let Ok(ep) = std::env::var("HF_ENDPOINT") {
        if !ep.starts_with("http://") && !ep.starts_with("https://") {
            anyhow::bail!("HF_ENDPOINT must be a full URL, got: {ep}");
        }
    }
    let cache = hf_hub::Cache::default().path().parent().unwrap().to_path_buf();
    std::fs::create_dir_all(&cache)
        .with_context(|| format!("HF cache dir not writable: {cache:?}"))?;
    Ok(())
}

Try / catch

// in the caller: distinguish config errors (do not retry) from transfer errors (retry/mirror)
let err = self.download_hf_model(&info, repo, rev).await.unwrap_err();
if err.to_string().contains("Failed to init Hugging Face API") {
    // config problem — surface an actionable message, skip mirror fallback
    return Err(err.context("Check HF_ENDPOINT / HF_HOME / proxy env vars"));
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of cjpais/Handy@ba10ce1943 (2026-08-17). Data as JSON: /api/errors/94fcfa1cda0ac772. Report an issue: GitHub.

Appendix: source

Thrown at src-tauri/src/managers/model.rs:1977

            let stream_count = ATTEMPT_STREAMS[attempt - 1];
            info!(
                "HF download attempt {}/{} for {} using {} concurrent stream(s)",
                attempt,
                ATTEMPT_STREAMS.len(),
                model_id,
                stream_count
            );

            // Fresh client per attempt so a wedged connection from the previous
            // try can't poison the retry.
            let api = ApiBuilder::from_env()
                // Ignore cached and environment-provided credentials. A stale token
                // can make otherwise-public downloads fail authentication.
                .with_token(None)
                .with_progress(false)
                .with_max_files(stream_count)
                .build()
                .map_err(|e| anyhow::anyhow!("Failed to init Hugging Face API: {}", e))?;
            let repo = api.repo(Repo::with_revision(
                repo_id.clone(),
                RepoType::Model,
                revision.clone(),
            ));
            let progress = HfDownloadProgress::new(self.app_handle.clone(), model_id.clone());

            // hf-hub has no internal timeouts, so a wedged connection would
            // otherwise hang this attempt forever and neither the retry loop
            // nor the mirror fallback would ever fire. The watchdog cancels a
            // per-attempt child token when progress goes stale; a user cancel
            // on the parent propagates through the same child.
            let attempt_token = cancel_token.child_token();
            let watchdog = tokio::spawn({
                let probe = progress.clone();
                let attempt_token = attempt_token.clone();
                async move {
                    loop {

View on GitHub (pinned to ba10ce1943)