openvinotoolkit / openvinotoolkit/model_server
Download progress bar divides by zero without Content-Length and prints ~2.1 billion spaces per tick
@rasapala is already working on this.
Since Sep 15, 2026.
- Dominant language
- C++
- Stars
- 931
- Forks
- 277
- Avg merge
- 2d 13h
- Merged PRs (30d)
- 68
Description
Describe the bug
When a model download is served over a response with no Content-Length (chunked transfer encoding), the progress bar divides by zero and then prints roughly 2.1 billion spaces per progress tick. The pull appears to hang; the transfer itself is fine.
src/pull_module/curl_downloader.cpp:49-63:
static void print_progress(size_t count, size_t max, bool first_run, size_t elapsed_time) {
float progress = (float)count / max; // :50 max == 0 -> inf
if (!first_run && progress < 0.01 && count > 0)
return;
const int bar_width = 50;
int bar_length = progress * bar_width; // :55 (int)inf is undefined
...
for (i = bar_length; i < bar_width; ++i) { // :62
printf(" ");
}
max is libcurl's dltotal, which is 0 when the server does not announce a size. The guard in progress_callback (src/pull_module/curl_downloader.cpp:133-135) only short-circuits while dltotal == dlnow:
if ((dltotal == dlnow) && dltotal < 10000) {
return 0;
}
Once any bytes have arrived, dlnow > 0 == dltotal, so the callback falls through to print_progress(dlnow, 0, ...).
progress becomes inf. Converting an infinite float to int is undefined behaviour; on x86-64 it yields INT_MIN. The bar-fill loop at :59 therefore does not run at all, and the padding loop at :62 runs from INT_MIN to bar_width.
Compiling exactly those two lines and counting the padding iterations:
bar_length = -2147483648
padding loop would run 2147483698 times
That is ~2.1 billion printf(" ") calls on every tick, and progress_callback prints once a second.
Related, same function: bar_length is never clamped, so a server reporting more bytes than it announced (dlnow > dltotal, which happens with some content encodings) makes progress > 1 and overruns the bar in the other direction.
To Reproduce
Point a pull at any HTTP endpoint that responds with Transfer-Encoding: chunked and no Content-Length — a local mirror, a proxy, an internal registry, or a plain python3 -m http.server in front of the artifacts:
ovms --pull --source_model <model> --model_repository_path /tmp/repo
The download proceeds but the console stops being usable and the process appears stuck.
Expected behavior
With an unknown total size, print a running byte count (or a spinner) rather than a percentage bar, and never divide by max when max == 0. The bar length should also be clamped to the bar width so no ratio can drive the loops out of range.
Logs
Console output only — the terminal fills with whitespace and the \r-based progress line never completes.
Configuration
- OVMS version:
main@fadb3314 - Any
--pullagainst a source served withoutContent-Length - CPU
- N/A
- Any model
Additional context
Suggested fix: early-return from print_progress when max == 0 and report bytes received instead, and move the bar arithmetic into a small helper that returns 0 for an unknown total and clamps to [0, barWidth].
I have a patch for this and will open a PR shortly.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.