Skip to content

CURLE_PARTIAL_FILE is reported on HTTP/3 stream reset even if the entire body was received #22270

Description

@Lubrsi

I did this

I loaded https://cdn.7tv.app/emote/01F6MQ33FG000FFJ97ZB8MWV52/1x.webp in Ladybird with a small patch:

diff --git a/Services/RequestServer/Request.cpp b/Services/RequestServer/Request.cpp
index 29d7ef9bb4e..5227f2ed2ac 100644
--- a/Services/RequestServer/Request.cpp
+++ b/Services/RequestServer/Request.cpp
@@ -940,7 +940,7 @@ void Request::handle_fetch_state()
 
     set_option(CURLOPT_CUSTOMREQUEST, m_method.characters());
     set_option(CURLOPT_FOLLOWLOCATION, 0);
-    if constexpr (CURL_DEBUG) {
+    if constexpr (1) {
         set_option(CURLOPT_VERBOSE, 1);
     }
 
@@ -1157,6 +1157,8 @@ size_t Request::on_data_received(void* buffer, size_t size, size_t nmemb, void*
     auto total_size = size * nmemb;
     ReadonlyBytes bytes { static_cast<u8 const*>(buffer), total_size };
 
+    dbgln("received {} bytes", total_size);
+
     auto result = [&] -> ErrorOr<void> {
         TRY(request.m_response_buffer.write_some(bytes));
         return request.write_queued_bytes_without_blocking();
Which resulted in: * Added cdn.7tv.app:443:37.27.171.118,37.27.171.120,37.27.171.107,37.27.171.119,37.27.171.106,37.27.171.117,37.27.171.108,37.27.171.109,37.27.171.121,37.27.171.122,::ffff:37.27.171.118,::ffff:37.27.171.120,::ffff:37.27.171.107,::ffff:37.27.171.119,::ffff:37.27.171.106,::ffff:37.27.171.117,::ffff:37.27.171.108,::ffff:37.27.171.109,::ffff:37.27.171.121,::ffff:37.27.171.122 to DNS cache * Hostname cdn.7tv.app was found in DNS cache * Host cdn.7tv.app:443 was resolved. * IPv6: ::ffff:37.27.171.118, ::ffff:37.27.171.120, ::ffff:37.27.171.107, ::ffff:37.27.171.119, ::ffff:37.27.171.106, ::ffff:37.27.171.117, ::ffff:37.27.171.108, ::ffff:37.27.171.109, ::ffff:37.27.171.121, ::ffff:37.27.171.122 * IPv4: 37.27.171.118, 37.27.171.120, 37.27.171.107, 37.27.171.119, 37.27.171.106, 37.27.171.117, 37.27.171.108, 37.27.171.109, 37.27.171.121, 37.27.171.122 * Trying [::ffff:37.27.171.118]:443... * SSL Trust Anchors: * Native: Apple SecTrust * OpenSSL default paths (fallback) * Trying 37.27.171.118:443... * Trying [::ffff:37.27.171.118]:443... * SSL Trust Anchors: * Native: Apple SecTrust * OpenSSL default paths (fallback) * ALPN: curl offers h2,http/1.1 * SSL Trust Anchors: * Native: Apple SecTrust * OpenSSL default paths (fallback) * SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 / x25519 / id-ecPublicKey * Server certificate: * subject: CN=cdn.7tv.app * start date: May 14 08:00:33 2026 GMT * expire date: Aug 12 08:00:32 2026 GMT * issuer: C=US; O=Let's Encrypt; CN=E8 * Certificate level 0: Public key type EC/prime256v1 (256/128 Bits/secBits), signed using ecdsa-with-SHA384 * Certificate level 1: Public key type EC/secp384r1 (384/192 Bits/secBits), signed using sha256WithRSAEncryption * Certificate level 2: Public key type RSA (4096/152 Bits/secBits), signed using sha256WithRSAEncryption * subjectAltName: "cdn.7tv.app" matches cert's "cdn.7tv.app" * OpenSSL verify result: 0 * SSL certificate verified via OpenSSL. * Established connection to cdn.7tv.app (37.27.171.118 port 443) from 192.168.1.105 port 65025 * using HTTP/3 * [HTTP/3] [0] OPENED stream for https://cdn.7tv.app/emote/01F6MQ33FG000FFJ97ZB8MWV52/1x.webp * [HTTP/3] [0] [:method: GET] * [HTTP/3] [0] [:scheme: https] * [HTTP/3] [0] [:authority: cdn.7tv.app] * [HTTP/3] [0] [:path: /emote/01F6MQ33FG000FFJ97ZB8MWV52/1x.webp] * [HTTP/3] [0] [accept-encoding: deflate, gzip, br, zstd] * [HTTP/3] [0] [accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8] * [HTTP/3] [0] [accept-language: en-GB,en-US,en] * [HTTP/3] [0] [sec-fetch-dest: document] * [HTTP/3] [0] [sec-fetch-mode: navigate] * [HTTP/3] [0] [sec-fetch-site: cross-site] * [HTTP/3] [0] [user-agent: Mozilla/5.0 (macOS; AArch64) Ladybird/1.0 Chrome/146.0.0.0 AppleWebKit/537.36 Safari/537.36] > GET /emote/01F6MQ33FG000FFJ97ZB8MWV52/1x.webp HTTP/3 Host: cdn.7tv.app Accept-Encoding: deflate, gzip, br, zstd Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: en-GB,en-US,en Sec-Fetch-Dest: document Sec-Fetch-Mode: navigate Sec-Fetch-Site: cross-site User-Agent: Mozilla/5.0 (macOS; AArch64) Ladybird/1.0 Chrome/146.0.0.0 AppleWebKit/537.36 Safari/537.36
  • Request completely sent off
    < HTTP/3 200
    < content-type: image/webp
    < content-length: 94822
    < x-7tv-cache-hits: 458307
    < x-7tv-cache: hit
    < age: 1649394
    < cache-control: public, max-age=31536000, s-maxage=86400, immutable
  • Added alt-svc: cdn.7tv.app:443 over h3
    < alt-svc: h3=":443"; ma=2592000
    < x-7tv-cdn-pod: cdn-klnfz
    < x-7tv-cdn-node: cdn-2
    < server: SevenTV
    < vary: origin, access-control-request-method, access-control-request-headers
    < access-control-allow-origin: *
    < access-control-expose-headers: *
    <
    275233.975 RequestServer(30387): received 1158 bytes
    275233.975 RequestServer(30387): received 1158 bytes
    275233.975 RequestServer(30387): received 1158 bytes
    275233.975 RequestServer(30387): received 2316 bytes
    275233.975 RequestServer(30387): received 1158 bytes
    275233.975 RequestServer(30387): received 1158 bytes
    275233.975 RequestServer(30387): received 1158 bytes
    275234.016 RequestServer(30387): received 1279 bytes
    275234.016 RequestServer(30387): received 1284 bytes
    275234.020 RequestServer(30387): received 1284 bytes
    275234.020 RequestServer(30387): received 1284 bytes
    275234.020 RequestServer(30387): received 1284 bytes
    275234.020 RequestServer(30387): received 1284 bytes
    275234.020 RequestServer(30387): received 1282 bytes
    275234.020 RequestServer(30387): received 1282 bytes
    275234.021 RequestServer(30387): received 1282 bytes
    275234.022 RequestServer(30387): received 1282 bytes
    275234.022 RequestServer(30387): received 1282 bytes
    275234.022 RequestServer(30387): received 971 bytes
    275234.022 RequestServer(30387): received 311 bytes
    275234.022 RequestServer(30387): received 1282 bytes
    275234.022 RequestServer(30387): received 1282 bytes
    275234.022 RequestServer(30387): received 1282 bytes
    275234.022 RequestServer(30387): received 1282 bytes
    275234.062 RequestServer(30387): received 1338 bytes
    275234.062 RequestServer(30387): received 1345 bytes
    275234.062 RequestServer(30387): received 1345 bytes
    275234.062 RequestServer(30387): received 4035 bytes
    275234.068 RequestServer(30387): received 1338 bytes
    275234.068 RequestServer(30387): received 1345 bytes
    275234.068 RequestServer(30387): received 199 bytes
    275234.068 RequestServer(30387): received 1146 bytes
    275234.068 RequestServer(30387): received 1345 bytes
    275234.068 RequestServer(30387): received 1345 bytes
    275234.068 RequestServer(30387): received 1345 bytes
    275234.068 RequestServer(30387): received 1345 bytes
    275234.081 RequestServer(30387): received 1345 bytes
    275234.081 RequestServer(30387): received 1345 bytes
    275234.081 RequestServer(30387): received 4035 bytes
    275234.081 RequestServer(30387): received 1345 bytes
    275234.081 RequestServer(30387): received 1788 bytes
    275234.081 RequestServer(30387): received 902 bytes
    275234.081 RequestServer(30387): received 1345 bytes
    275234.081 RequestServer(30387): received 1345 bytes
    275234.092 RequestServer(30387): received 1345 bytes
    275234.092 RequestServer(30387): received 1345 bytes
    275234.092 RequestServer(30387): received 1345 bytes
    275234.092 RequestServer(30387): received 1345 bytes
    275234.092 RequestServer(30387): received 2690 bytes
    275234.092 RequestServer(30387): received 1345 bytes
    275234.092 RequestServer(30387): received 1345 bytes
    275234.092 RequestServer(30387): received 1345 bytes
    275234.108 RequestServer(30387): received 1371 bytes
    275234.108 RequestServer(30387): received 1376 bytes
    275234.108 RequestServer(30387): received 1376 bytes
    275234.108 RequestServer(30387): received 1376 bytes
    275234.108 RequestServer(30387): received 1376 bytes
    275234.108 RequestServer(30387): received 2004 bytes
    275234.108 RequestServer(30387): received 748 bytes
    275234.108 RequestServer(30387): received 1376 bytes
    275234.108 RequestServer(30387): received 1376 bytes
    275234.108 RequestServer(30387): received 1376 bytes
    275234.114 RequestServer(30387): received 1376 bytes
    275234.114 RequestServer(30387): received 1940 bytes
    275234.114 RequestServer(30387): received 812 bytes
    275234.114 RequestServer(30387): received 1376 bytes
    275234.114 RequestServer(30387): received 1376 bytes
    275234.114 RequestServer(30387): received 1376 bytes
    275234.120 RequestServer(30387): received 2 bytes
  • HTTP/3 stream 0 reset by server (error 0x0 unknown)
  • Connection #0 to host cdn.7tv.app:443 left intact
    275234.120 WebContent(30392): ResourceLoader: Failed load of: "https://cdn.7tv.app/emote/01F6MQ33FG000FFJ97ZB8MWV52/1x.webp", Error: Request finished with error: Connection was closed before entire response was received, Duration: 525ms

Adding up the individual "received N bytes" lines, they total the reported Content-Length of 94822 and then the server is strange and resets the stream with an unknown error code. This check here turns any received body bytes into a CURLE_PARTIAL_FILE error:

curl/lib/vquic/cf-ngtcp2.c

Lines 463 to 466 in 6841d59

failf(data, "HTTP/3 stream %" PRId64 " reset by server (error 0x%" PRIx64
" %s)", stream->id, stream->error3,
Curl_vquic_h3_err_str(stream->error3));
return data->req.bytecount ? CURLE_PARTIAL_FILE : CURLE_HTTP3;

I expected the following

If the full body (as reported by Content-Length) has been received, the check above should return CURLE_OK, even though the server had acted strangely by resetting the stream.

curl/libcurl version

libcurl/8.21.0 OpenSSL/3.5.3 zlib/1.3.1 brotli/1.2.0 zstd/1.5.7 nghttp2/1.68.0 ngtcp2/1.24.0 nghttp3/1.17.0

operating system

Darwin Mac.lan 25.5.0 Darwin Kernel Version 25.5.0: Tue Jun 9 22:28:34 PDT 2026; root:xnu-12377.121.10~1/RELEASE_ARM64_T6041 arm64

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions