Skip to content

fix(model-install): use the current Hugging Face login when retrying installs - #9830

Merged
lstein merged 3 commits into
invoke-ai:mainfrom
Pfannkuchensack:fix/retry-install-hf-token
Oct 17, 2026
Merged

lstein merged 3 commits into
invoke-ai:mainfrom
Pfannkuchensack:fix/retry-install-hf-token

Conversation

@Pfannkuchensack

@Pfannkuchensack Pfannkuchensack commented Oct 12, 2026 •

Copy link
Copy Markdown
Member

Summary

If a gated Hugging Face model failed to download because no HF token was set, adding a valid token under API keys and clicking Retry install in the install queue still failed with the same 401. Only a brand-new install picked the token up.

Cause: _import_from_hf copied the server's HF login (huggingface_hub.get_token()) onto job.source.access_token once, at first import. restart_failed, restart_file, resume and startup restore all re-queue from that stored value. It was None if the user had no login on the first attempt, or an outdated token if they had since replaced it.

Fix: the HF login is no longer copied onto the source. _enqueue_remote_download reads it each time downloads are queued, so every retry path uses the current login.

  • A token sent with the install request (header or query) still takes precedence.
  • URL sources still never receive the HF login.

Side effect: the HF login no longer ends up in job.source. It is therefore no longer in the install-marker file on disk or in the source of model-install socket events. A queue resubmit also stops sending it back as if it were an explicit token.

Related Issues / Discussions

Closes #9829

QA Instructions

Automated:

  • uv run --no-sync pytest tests/app/services/model_install/: 158 passed.
  • uv run --no-sync pytest tests/app/routers/test_model_manager.py: passed, run together with the model-install tests before the final test additions.
  • ruff@0.11.2 check and format --check: clean.

New tests, which serve a "gated" file through a mock session that only answers requests carrying the expected Bearer token:

  • test_restart_failed_uses_hf_login_made_after_the_failure, with cases no_login and outdated_login. The first attempt fails with 401; after the login changes, restart_failed on the same job completes the install. Both cases fail on main.
  • test_download_authorization_while_logged_in_to_hf. An explicit token wins over the login, and a URL source sends no Authorization header while the user is logged in to HF.

Manual reproduction from the issue:

  1. Remove the HF token under API keys.
  2. Install a gated starter model (e.g. FLUX.1-schnell_ae) and wait for the gated/unauthorized error in the install queue.
  3. Add a valid HF token, expand the install queue and click Retry install. The download now succeeds.

I did not run this manual flow in a browser against a real gated repo; the behaviour is covered by the service-level tests above.

Review

Resolved: the first version of the code comment overstated the effect. It said the HF login no longer reaches clients at all. It does still reach them through download_parts[].access_token (below). The comment now only claims what the change guarantees. I also added tests for token precedence and for keeping the login away from URL sources.

Remaining limitations and follow-ups, all already present before this PR:

  • Token in admin API responses: DownloadJob.access_token is a serialized field, so download_parts[].access_token in the admin-only install endpoints (GET /api/v2/models/install and the job responses) still contains the token used for each download. Excluding it would change the API contract, so it is left for a separate change.
  • Civitai: the Civitai key lives only in browser storage. A key added after a failed Civitai download is not used by Retry install when the backend restarts the failed parts (restart_failed).
  • Gated repos fetched by download_and_cache_model: this path does not send the HF login, so a gated repo fetched that way gets a 401.

Compatibility / Rollout

  • Install markers written before this change may contain the HF login as access_token. On restore it is treated as an explicit token, exactly as before.
    • Consequence: if an install was paused or still downloading during the upgrade, and the login changes afterwards, retrying that one job keeps using the old token. Cancelling and reinstalling fixes it.
    • Failed installs delete their marker, so this only affects interrupted ones.
  • API: the shape is unchanged, so no schema regeneration is needed. For HF jobs, source.access_token is now only set when a token was sent with the install request. No frontend reads it for anything else; the webv2 resubmit now leaves the token to the backend.

Checklist

  • The PR has a short but descriptive title, suitable for a changelog
  • Meaningful regression coverage added / updated where needed; obsolete tests/code removed
  • Persisted-state and API changes include required migrations / compatibility validation
  • Relevant performance/efficiency opportunities considered; material claims have evidence
  • Material review findings resolved and relevant checks rerun
  • Documentation added / updated (if applicable)
  • Updated What's New copy (if doing a release after this PR)

View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.

…installs

Read the HF login each time downloads are queued instead of freezing it onto the
install source, so retry/resume after logging in no longer fails with 401.
Explicit tokens still take precedence; URL sources never receive the login.

Closes invoke-ai#9829
@github-actions github-actions Bot added python PRs that change python files services PRs that change app services python-tests PRs that change python tests labels Oct 12, 2026

@lstein lstein left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed at e7154fc. No blockers.

What I checked

  • Every path that queues downloads goes through _enqueue_remote_download: a new import, restart_failed, restart_file, resume_job, and restoring unfinished installs at startup. Reading the login there covers all of them, and no path reuses the old download jobs' saved token.
  • The file-list fetch for gated repos never read source.access_token; HfApi picks up the login on its own, so dropping the copy doesn't affect it.
  • Token priority is unchanged: explicit token from the request, then the HF login, then the download queue's remote_api_tokens lookup. (An empty-string token now falls through to the login instead of blocking it, which is an improvement.)
  • The HFModelSource check keeps the login away from URL sources.
  • The webv2 resubmit only sends source.access_token when set, so it now leaves login-based installs to the backend; the legacy frontend never sends one.
  • The tests catch regressions. Reverting to main's code fails both restart_failed cases; dropping the HF-only check fails url_source_never_gets_login; letting the login win over an explicit token fails explicit_token_wins_over_login.
  • The model-install, model-manager router and download suites pass (264 tests); ruff check and format are clean.

Non-blocking notes

  1. Install markers written before the upgrade can still hold the old login as an "explicit" token. This affects only interrupted installs and is already in the PR description.
  2. If HF_TOKEN is set in the server environment, get_token() returns it instead of the token saved under API keys, so a retry won't pick up a UI login change there. This was already the case before this PR.
  3. resume_job and startup restore aren't tested directly. They share the tested enqueue path, so the risk is low.

@lstein
lstein enabled auto-merge October 17, 2026 19:23
@lstein
lstein merged commit 27e324f into invoke-ai:main Oct 17, 2026
17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

python PRs that change python files python-tests PRs that change python tests services PRs that change app services

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[bug]: "Retry install" in model manager fails for gated models requiring a Hugging Face token after adding it

2 participants