Http2: stream closed and H3_REQUEST_CANCELLED

1. The problem I’m having:

Hi!

When I start a stream from my Jellyfin server, I get these errors.
The stream is starting and no problem what I can see, so is this an error I should care about?

2. Error messages and/or full log output:

{
  "level": "error",
  "ts": 1738858257.6554253,
  "logger": "http.handlers.reverse_proxy",
  "msg": "aborting with incomplete response",
  "upstream": "jellyfin:8096",
  "duration": 0.020272269,
  "request": {
    "client_ip": "##.##.##.##",
    "headers": {
      "Accept-Encoding": [
        "gzip, deflate, br"
      ],
      "Icy-Metadata": [
        "1"
      ],
      "User-Agent": [
        "Jellyfin Android/2.6.2 (Linux;Android 15) ExoPlayerLib/2.19.1"
      ],
      "X-Forwarded-For": [
        "##.##.##.##"
      ],
      "X-Forwarded-Host": [
        "jellyfin.domain.tld"
      ],
      "X-Forwarded-Proto": [
        "https"
      ]
    },
    "host": "jellyfin.domain.tld",
    "method": "GET",
    "proto": "HTTP/2.0",
    "remote_ip": "##.##.##.##",
    "remote_port": "28648",
    "tls": {
      "cipher_suite": 4865,
      "proto": "h2",
      "resumed": false,
      "server_name": "jellyfin.domain.tld",
      "version": 772
    },
    "uri": "/Videos/157fb2ae-1a95-2b35-7357-c29b556cbd50/stream?static=true&playSessionId=f583a822fdca4c0f81015c55cece4267&mediaSourceId=157fb2ae1a952b357357c29b556cbd50&deviceId=7693145f6bd466dafe622119623f416d9ccafc213afd09d9&streamOptions=%7B%7D"
  },
  "error": "writing: http2: stream closed"
}
{
  "level": "error",
  "ts": 1738858275.891016,
  "logger": "http.handlers.reverse_proxy",
  "msg": "aborting with incomplete response",
  "upstream": "jellyfin:8096",
  "duration": 0.030669804,
  "request": {
    "client_ip": "##.##.##.##",
    "headers": {
      "Accept-Encoding": [
        "identity"
      ],
      "Icy-Metadata": [
        "1"
      ],
      "Range": [
        "bytes=14651851754-"
      ],
      "User-Agent": [
        "Jellyfin Android/2.6.2 (Linux;Android 15) ExoPlayerLib/2.19.1"
      ],
      "X-Forwarded-For": [
        "##.##.##.##"
      ],
      "X-Forwarded-Host": [
        "jellyfin.domain.tld"
      ],
      "X-Forwarded-Proto": [
        "https"
      ]
    },
    "host": "jellyfin.domain.tld",
    "method": "GET",
    "proto": "HTTP/3.0",
    "remote_ip": "##.##.##.##",
    "remote_port": "28662",
    "tls": {
      "cipher_suite": 4865,
      "proto": "h3",
      "resumed": false,
      "server_name": "jellyfin.domain.tld",
      "version": 772
    },
    "uri": "/Videos/157fb2ae-1a95-2b35-7357-c29b556cbd50/stream?static=true&playSessionId=f583a822fdca4c0f81015c55cece4267&mediaSourceId=157fb2ae1a952b357357c29b556cbd50&deviceId=7693145f6bd466dafe622119623f416d9ccafc213afd09d9&streamOptions=%7B%7D"
  },
  "error": "writing: H3_REQUEST_CANCELLED"
}

3. Caddy version:

2.9.1

4. How I installed and ran Caddy:

Docker compose.

a. System environment:

Ubuntu server 22.04.5. Run all services in Docker.

c. Service/unit/compose file:

version: "3.7"
services:
  caddy:
    container_name: caddy
    # image: caddy:latest
    build: .
    restart: unless-stopped
    ports:
      - 80:80
      - 443:443
      - 443:443/udp
    volumes:
      - ./data/Caddyfile:/etc/caddy/Caddyfile
      - ./data/data:/data
      - ./data/config:/config
    networks:
      - jellyfin_default
      - app2_default
      - app3_default
      - app4_default
networks:
  jellyfin_default:
    external: true
  app2_default:
    external: true
  app3_default:
    external: true
  app4_default:
    external: true

d. My complete Caddy config:

*.domain.tld {
    tls {
        dns cloudflare 123456789
    }

    @jellyfin host jellyfin.domain.tld
    handle @jellyfin {
        reverse_proxy jellyfin:8096
    }

    @app2 host app2.domain.tld
    handle @app2 {
        reverse_proxy app2:123
    }

    @app3 host app3.domain.tld
    handle @app3 {
        reverse_proxy app3:123
    }

    @app4 host app4.domain.tld
    handle @app4 {
        reverse_proxy app4:123
    }

    # Fallback for otherwise unhandled domains
    handle {
        abort
    }
}

5. Links to relevant resources:

Something is closing the HTTP2 or HTTP/3.0 stream after Jellyfin receives the request. I presume your ports for 80 and 443 are open on the firewall. What is the network configuration for jellyfin_default? Can you post your Caddy logs?

jellyfin_default is running in bridge mode and in my Caddyfile I use the docker hostname of jellyfin; jellyfin:8096.

The logs:

2025-02-10T15:40:21.563093413Z {"level":"debug","ts":1739202021.562953,"logger":"http.handlers.reverse_proxy","msg":"upstream roundtrip","upstream":"jellyfin:8096","duration":0.016771721,"request":{"remote_ip":"10.0.0.20","remote_port":"46728","client_ip":"10.0.0.20","proto":"HTTP/2.0","method":"GET","host":"jf.domain.tld","uri":"/Videos/157fb2ae-1a95-2b35-7357-c29b556cbd50/stream?static=true&playSessionId=78aa361f3ec14ab0ae45b41fb7171415&mediaSourceId=157fb2ae1a952b357357c29b556cbd50&deviceId=7693145f6bd466dafe622119623f416d9ccafc213afd09d9&streamOptions=%7B%7D","headers":{"X-Forwarded-For":["10.0.0.20"],"X-Forwarded-Proto":["https"],"X-Forwarded-Host":["jf.domain.tld"],"User-Agent":["Jellyfin Android/2.6.2 (Linux;Android 15) ExoPlayerLib/2.19.1"],"Accept-Encoding":["gzip, deflate, br"],"Icy-Metadata":["1"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"h2","server_name":"jf.domain.tld"}},"headers":{"Server":["Kestrel"],"Accept-Ranges":["bytes"],"Last-Modified":["Sun, 24 Dec 2023 22:11:18 GMT"],"X-Response-Time-Ms":["16.4132"],"Content-Length":["120576935820"],"Content-Type":["video/x-matroska"],"Date":["Mon, 10 Feb 2025 15:40:20 GMT"]},"status":200}
2025-02-10T15:40:21.587617467Z {"level":"error","ts":1739202021.5875251,"logger":"http.handlers.reverse_proxy","msg":"aborting with incomplete response","upstream":"jellyfin:8096","duration":0.016771721,"request":{"remote_ip":"10.0.0.20","remote_port":"46728","client_ip":"10.0.0.20","proto":"HTTP/2.0","method":"GET","host":"jf.domain.tld","uri":"/Videos/157fb2ae-1a95-2b35-7357-c29b556cbd50/stream?static=true&playSessionId=78aa361f3ec14ab0ae45b41fb7171415&mediaSourceId=157fb2ae1a952b357357c29b556cbd50&deviceId=7693145f6bd466dafe622119623f416d9ccafc213afd09d9&streamOptions=%7B%7D","headers":{"X-Forwarded-For":["10.0.0.20"],"X-Forwarded-Proto":["https"],"X-Forwarded-Host":["jf.domain.tld"],"User-Agent":["Jellyfin Android/2.6.2 (Linux;Android 15) ExoPlayerLib/2.19.1"],"Accept-Encoding":["gzip, deflate, br"],"Icy-Metadata":["1"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"h2","server_name":"jf.domain.tld"}},"error":"writing: http2: stream closed"}

This tells me that Caddy is properly forwarding its request. The client requests the video stream and the server responds with a 200 OK, indicating it’s sending the file. However, the connection is abruptly closed during transmission.

I believe this issue lies with Jellyfin and the configuration or plugins. Otherwise, it’s the networking or firewalls. There’s more to the networking configuration than just the bridge mode. You specified it as an external network, which means it was created outside of the compose.yaml file. Use

docker network inspect jellyfin_default

to show the configuration of it. Is Jellyfin on the host machine or also configured in Docker Compose?

Jellyfin also runs with docker compose.

[
    {
        "Name": "jellyfin_default",
        "Id": "70100b07194598e603a8957be94c86a4fb170f882cc0180332d4e0b80b40ded9",
        "Created": "2023-12-31T01:40:11.838847981+01:00",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {
                    "Subnet": "192.168.160.0/20",
                    "Gateway": "192.168.160.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {
            "23e6bcd30df15f1c5b992d620144f2bef0eee14e4735d26ef1754e83a5479822": {
                "Name": "jellyseerr",
                "EndpointID": "6383ded2ec8033a4b84cd8d1fc093b87d1a1fd1761587944cd4387483be073e5",
                "MacAddress": "02:42:c0:a8:a0:02",
                "IPv4Address": "192.168.160.2/20",
                "IPv6Address": ""
            },
            "27ed866036abb3e9ccd0c55f905b9ae0b8d37cc227ba307cece40b779d6ab828": {
                "Name": "Jellyfin",
                "EndpointID": "3fca165a0dbb1043981bd76974bff082daec73dd161215e4d3ab1692a640352b",
                "MacAddress": "02:42:c0:a8:a0:03",
                "IPv4Address": "192.168.160.3/20",
                "IPv6Address": ""
            },
            "f5d00ac7a85ceee3ecb7aa86a70dfd498a473364fbe473d0cfc442802c7abde6": {
                "Name": "caddy",
                "EndpointID": "af6a663e3c674b04d365db28bdf8fc5c38773c9d241f20b19d0d973e6e72a9fe",
                "MacAddress": "02:42:c0:a8:a0:04",
                "IPv4Address": "192.168.160.4/20",
                "IPv6Address": ""
            }
        },
        "Options": {},
        "Labels": {
            "com.docker.compose.network": "default",
            "com.docker.compose.project": "jellyfin",
            "com.docker.compose.version": "2.21.0"
        }
    }
]

Can you post your entire Jellyfin configuration within your compose.yaml file? I’d like to try to help you with Jellyfin even though I’m still thinking this issue isn’t caused by Caddy.

I asked about this on their discord but we got nowhere.

Could it have something to do with Jellyfin running HTTP/1.1?

version: "3"
services:
  jellyfin:
    container_name: jellyfin
    image: jellyfin/jellyfin:latest
    volumes:
      - ./data/config:/config
      - ./data/cache:/cache
      - /mnt/data:/data
    devices:
      - /dev/dri/renderD128:/dev/dri/renderD128
      - /dev/dri/card0:/dev/dri/card0
    ports:
      - 8096:8096
    restart: unless-stopped
    user: 1003:1003
    group_add:
      - "44"
      - "109"

Edit:
If I turn off h2 and h3 in Caddy I get this error:

{
  "level": "error",
  "ts": 1739382512.8459184,
  "logger": "http.handlers.reverse_proxy",
  "msg": "aborting with incomplete response",
  "upstream": "jellyfin:8096",
  "duration": 0.002104944,
  "request": {
    "client_ip": "10.0.0.20",
    "headers": {
      "Accept-Encoding": [
        "gzip, deflate, br"
      ],
      "Icy-Metadata": [
        "1"
      ],
      "User-Agent": [
        "Jellyfin Android/2.6.2 (Linux;Android 15) ExoPlayerLib/2.19.1"
      ],
      "X-Forwarded-For": [
        "10.0.0.20"
      ],
      "X-Forwarded-Host": [
        "jellyfin.domain.tld"
      ],
      "X-Forwarded-Proto": [
        "https"
      ]
    },
    "host": "jellyfin.domain.tld",
    "method": "GET",
    "proto": "HTTP/1.1",
    "remote_ip": "10.0.0.20",
    "remote_port": "47080",
    "tls": {
      "cipher_suite": 4865,
      "proto": "http/1.1",
      "resumed": false,
      "server_name": "jellyfin.domain.tld",
      "version": 772
    },
    "uri": "/Videos/157fb2ae-1a95-2b35-7357-c29b556cbd50/stream?static=true&playSessionId=9781751d620b491fb35c229828eff78a&mediaSourceId=157fb2ae1a952b357357c29b556cbd50&deviceId=7693145f6bd466dafe622119623f416d9ccafc213afd09d9&streamOptions=%7B%7D"
  },
  "error": "writing: write tcp 172.28.0.3:443->10.0.0.20:47080: write: connection reset by peer"
}

I’m curious. If you use network_mode: 'host' instead of your ports, does this change the fact it’s closing the stream? I’d re-enable HTTP2 and 3.

Did you also enable debug logging?

No difference with host mode.

Here is a debug logging of Jellyfin:

I think around row 595 is when I’m starting the stream.

I see it’s a mkv codec. Is the device trying to play this capable of decoding it?

Yes it’s capable to do that, no need to transcode, it’s direct playing. But I noticed that when I force it to transcode it doesn’t give me the “H3_REQUEST_CANCELLED”-error when the stream is starting, only when I stop the stream. When direct play it give me the error on both start and stop.

That’s… interesting. I’d try removing HTTP3 and doing it again. If the log output is something new, let me know.

@Mohammed90 I’m out of ideas. Are you able to help?

Honestly, I’ve been looking at this topic on and off, and I’m not sure what’s happening. Request cancellation in-and-of itself isn’t an error, but it appears to be cancelled inadvertently or unintentionally. I don’t use Jellyfin so I’m not familiar with the options available on the client, but is there something about network bandwidth or performance to tweak?

I’ve two other reports of the same error, but I don’t know if they’re the same issue:

If you’re good with networks, try using Wireshark to trace what exactly happens. It’ll be hard for me to investigate it myself because the reports are not reproducible.

1 Like

With h3 disabled I only get “writing: http2: stream closed”.

Nah, but I will for sure try. Never used Wireshark, but maybe a good time to learn. :smile:

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.