Say why a Xet download sits at 0% until it finishes by shimmyshimmer · Pull Request #9159 · unslothai/unsloth · GitHub
Skip to content

Say why a Xet download sits at 0% until it finishes - #9159

Merged
shimmyshimmer merged 6 commits into
mainfrom
studio/xet-progress-notice
Aug 18, 2026
Merged

Say why a Xet download sits at 0% until it finishes#9159
shimmyshimmer merged 6 commits into
mainfrom
studio/xet-progress-notice

Conversation

@shimmyshimmer

Copy link
Copy Markdown
Member

Problem

Xet is the most common download complaint we get, and the complaint is usually wrong: the download is fine. Xet fetches a model as parallel chunks and commits them in batches, and progress is read from the bytes allocated on disk, so a healthy Xet transfer reads 0% for a minute and then completes in one jump. From the outside that is indistinguishable from a hang, so people cancel a download that was working.

Change

The first three model downloads that start on Xet now explain it:

Download progress may appear slow, but the download is still running.

Hugging Face Xet enables faster downloads by fetching model data as parallel chunks. Because chunks are written out of order and committed in batches, the progress indicator may appear stuck or update unevenly even while data is actively downloading.

For smoother progress updates, go to 'Model Hub' and switch transport to HTTP.

Details worth knowing:

  • It fires on the transport the backend confirms the job actually started on, not the requested one, so a download that resolved to HTTP never gets an explanation that does not apply to it.
  • Three downloads, then it stops. The count lives in localStorage with an in-memory carry, so a browser that refuses the write (private mode, quota) does not turn this into a toast on every download.
  • Adopted jobs are excluded. Attaching to a download another tab started is not a download the user just began.
  • Model downloads only. Datasets behave the same way on Xet but are a quieter path, so they stay silent for now.
  • It holds for 8s rather than the Toaster's 5s default, which is what the longer explanatory toasts in chat use.

Testing

  • studio/frontend/tests/xet-progress-notice.test.ts covers the gate (Xet and models only), the three-download limit, the stored count across sessions, junk stored values, and the unwritable-storage carry.
  • Full frontend suite passes, 3686 tests.
  • tsc -b clean, eslint and biome clean on the touched files, and vite build succeeds.

Xet fetches a model as parallel chunks and commits them in batches, so
progress reads 0% for a while and then completes at once. That looks like a
hung download, and it is the most common complaint about Xet.

Toast the reason on the first three model downloads that start on Xet, and
point at the HTTP transport in Model Hub for anyone who wants steadier
progress. The count is stored, with an in-memory carry so a browser that
refuses the write does not repeat the toast on every download.
chatgpt-codex-connector[bot]

This comment was marked as resolved.

The start endpoints accept a second client for a job already running and
report that job's transport, which is indistinguishable from a fresh Xet
start. Both now say which it was, so an attaching tab neither shows the
notice nor spends one of the three.
chatgpt-codex-connector[bot]

This comment was marked as resolved.

The routes set response_model, so a key the schema does not name is dropped
before it reaches the browser. The flag shipped in the handler but never in
the response, leaving the notice gate reading undefined.
chatgpt-codex-connector[bot]

This comment was marked as resolved.

A cancel can land while the start POST is in flight: the start still comes
back accepted, so the notice promised a running download the user had just
stopped, and spent one of the three doing it.
@shimmyshimmer

Copy link
Copy Markdown
Member Author

chatgpt-codex-connector[bot]

This comment was marked as resolved.

The reject branch also covers a cross-variant conflict and an in-progress
delete, which join nothing. Both flags now come from the adoptable verdict.
@shimmyshimmer

Copy link
Copy Markdown
Member Author

@codex review

chatgpt-codex-connector[bot]

This comment was marked as resolved.

Two tabs starting a download at the same moment both read the count before
either wrote it, so a fourth toast could appear. Web Locks make the read and
the write one step; browsers without them keep the old bounded race.
@shimmyshimmer

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Another round soon, please!

Reviewed commit: 66f5f879fa

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@shimmyshimmer
shimmyshimmer merged commit 3bf9bff into main Aug 18, 2026
37 of 39 checks passed
@shimmyshimmer
shimmyshimmer deleted the studio/xet-progress-notice branch August 18, 2026 09:31
@danielhanchen

Copy link
Copy Markdown
Member

Recording why this was reverted in #9293, since the reason was never written down and it came up again when we looked at re-landing it.

The notice is fine in itself. The problem is where it renders. Sonner puts it top right at 356x235, and that is where the Model hub keeps its own controls, so for the 8 seconds the toast is up those controls sit underneath it and cannot be clicked.

Measured on two isolated installs built from this PR's merge base 17665d4a8 and its head 66f5f879f, transport pinned to Xet on both sides, HF cache isolated, four consecutive model downloads per side. For every interactive element whose rectangle intersects the toast, the check is document.elementFromPoint at that element's own centre: if it resolves into [data-sonner-toast], the control is not clickable.

Download Notice on head Controls blocked on head Controls blocked on base
1 yes 4 0
2 yes 6 0
3 yes 6 0
4 no 0 0

Blocked on downloads 2 and 3: Capability filter, Sort models, the Models and Datasets tabs, Copy repository ID, View repository. On the base install the toast never appears and nothing is ever blocked.

Download 2, base on the left and head on the right. The filter row, the sort dropdown, the two icon buttons and the RAM and CPU chips are all present on the left and hidden on the right:

download 2

Download 4, after XET_NOTICE_LIMIT is spent. No toast, and the two sides match again:

download 4

Two things worth ruling out for anyone re-landing this, because both look plausible and both are wrong. The download progress card is not affected: it is bottom right at y=863 while the toast is top right at y=12, and the measured overlap is 0. The transport pills are not affected either: they sit at x=737 to 861 and the toast starts at x=1132, and the pill hit-tests to itself on every download. Only the controls listed above are actually covered.

Worth noting that the notice ends with "go to 'Model Hub' and switch transport to HTTP", so the advice arrives at the same moment as a toast that covers part of the toolbar the user is being sent to.

main is unaffected today. The revert in #9293 holds and the notice component, the xetNoticeCount key and XET_NOTICE_LIMIT are all absent as of 2d5d4d8b3.

If this is re-landed, the fix is placement rather than content. Rendering the notice inside the download panel it describes, or anywhere that is not on top of the hub toolbar, keeps the explanation without taking the controls away for 8 seconds at a time.

danielhanchen added a commit that referenced this pull request Aug 25, 2026
…lbar (#9658)

* Reapply "Say why a Xet download sits at 0% until it finishes (#9159)" (#9293)

This reverts commit e326033.

* Shrink the Xet notice so it clears the hub toolbar

The notice itself was fine. Its size was not: 62 characters of title and
330 of description rendered 235px tall in the top-right corner, which is
where the Model hub keeps its own toolbar. While the toast was up the
capability filter, the sort dropdown, the Models and Datasets tabs and
the repo action icons sat underneath it. Hit testing each control's own
centre point put 4 to 6 of them inside the toast, so they could not be
clicked, three times per install at 8s each. That is what #9293 reverted.

Title is now 19 characters and the description 149, in plain language and
one paragraph, which drops the pre-line class the old blank line needed.
The character budget is asserted in the tests rather than left as a
convention, since the failure is invisible to unit tests and to a
screenshot taken at the wrong viewport.

* Drop the HTTP hint: it left a 0.5px margin, not a margin

The measured pair showed the shortened toast clearing the toolbar, but
only just. At 149 characters it rendered 114.5px tall, bottom edge at
y=126.5, against a hub filter row whose centre sits at y=127. Every
control hit-tested as clickable, so the check passed, by half a pixel.
A longer translation, a different font stack or a zoom level would have
put it back over the row and re-broken what #9293 reverted.

At 101 characters the toast ends near y=100 and does not reach the row
at all, so the result stops depending on sub-pixel layout. The budget in
the tests drops from 170 to 110, which now rejects the 149-char version
rather than accepting it.

The transport control is two clicks away in Model Hub and discoverable
without being told. A toast that eats the toolbar is not worth it.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants