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
- 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
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
- 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
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
- Download failed from Hugging Face
- Hugging Face download failed after
- transfer stalled: no progress for
- Complete model file not found in HF cache or models dir
- Model is currently downloading
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)