Skip to content

Range resume corrupts files: a 200 response that serves the requested range is treated as a restart #50

Description

@ryanzhangtianran

The ModelScope file endpoint answers a Range request with 200 OK instead of 206 Partial Content, even though it does serve only the requested slice:

curl -sL -o /dev/null -D -  
-H 'Range: bytes=1000000000-1000000099' 
'https://www.modelscope.cn/api/v1/models/Qwen/Qwen3-VL-8B-Instruct/repo?Revision=master&FilePath=model-00002-of-00004.safetensors'

Returned:

HTTP/1.1 200 OK
Server: nginx/1.24.0
Content-Length: 100
Accept-Ranges: bytes
Content-Range: bytes 1000000000-1000000099/4915962496

100 bytes requested, 100 bytes returned, correct Content-Range — the range is honoured. Only the status code is wrong. Per RFC 9110 §15.3.7 this should be 206.

And it may break the SDK

_download_with_resume decides whether a response is a resume from the status code alone.

is_resumed = resp.status_code == 206
...
mode = "ab" if is_resumed else "wb"
if not is_resumed:
    existing_size = 0

So on every resume is_resumed is False, and the .incomplete file is truncated (wb) — but the server is still sending from the requested offset, so mid-file bytes get written at offset 0. The result is a file that is both short and shifted. Nothing notices until the SHA-256 check rejects it, which then deletes the file and restarts from byte 0.

In one run over an unreliable link, /proc//io showed 30.9 GB written for a 17.5 GB model, with 6.1 GB retained.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions