Repository navigation
fix(model-install): use the current Hugging Face login when retrying installs - #9830
Merged
lstein merged 3 commits intoOct 17, 2026
Merged
Conversation
…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
lstein
approved these changes
Oct 17, 2026
lstein
left a comment
Collaborator
There was a problem hiding this comment.
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;HfApipicks 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_tokenslookup. (An empty-string token now falls through to the login instead of blocking it, which is an improvement.) - The
HFModelSourcecheck keeps the login away from URL sources. - The webv2 resubmit only sends
source.access_tokenwhen 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_failedcases; dropping the HF-only check failsurl_source_never_gets_login; letting the login win over an explicit token failsexplicit_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
- 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.
- If
HF_TOKENis 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. resume_joband startup restore aren't tested directly. They share the tested enqueue path, so the risk is low.
lstein
enabled auto-merge
October 17, 2026 19:23
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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_hfcopied the server's HF login (huggingface_hub.get_token()) ontojob.source.access_tokenonce, at first import.restart_failed,restart_file, resume and startup restore all re-queue from that stored value. It wasNoneif 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_downloadreads it each time downloads are queued, so every retry path uses the current 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 thesourceof 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 checkandformat --check: clean.New tests, which serve a "gated" file through a mock session that only answers requests carrying the expected
Bearertoken:test_restart_failed_uses_hf_login_made_after_the_failure, with casesno_loginandoutdated_login. The first attempt fails with 401; after the login changes,restart_failedon the same job completes the install. Both cases fail onmain.test_download_authorization_while_logged_in_to_hf. An explicit token wins over the login, and a URL source sends noAuthorizationheader while the user is logged in to HF.Manual reproduction from the issue:
FLUX.1-schnell_ae) and wait for the gated/unauthorized error in the install queue.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:
DownloadJob.access_tokenis a serialized field, sodownload_parts[].access_tokenin the admin-only install endpoints (GET /api/v2/models/installand 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.restart_failed).download_and_cache_model: this path does not send the HF login, so a gated repo fetched that way gets a 401.Compatibility / Rollout
access_token. On restore it is treated as an explicit token, exactly as before.source.access_tokenis 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
What's Newcopy (if doing a release after this PR)Need help on this PR? Tag
@codesmith-botwith what you need.