[GH-ISSUE #509] resume() sends a second HTTP response (400) after a successful resume #311

Closed
opened 2026-10-15 12:59:33 +00:00 by bsakaguchi · 0 comments
Owner

Originally created by @jdonaldson on GitHub (Sep 21, 2026).
Original GitHub issue: https://github.com/games-on-whales/wolf/issues/509

Summary

endpoints::https::resume() writes two complete HTTP responses onto the same connection when a resume succeeds: the 200 + resume XML, immediately followed by a 400 from server_error(). The trailing server_error() call sits at function scope instead of inside the else.

Code

src/moonlight-server/rest/endpoints.hpp#L442-L474 (stable @ 2273b2e):

  if (old_session) {
    ...
    auto xml = moonlight::launch_resume(rtsp_ip, std::to_string(get_port(state::RTSP_SETUP_PORT)));
    send_xml<SimpleWeb::HTTPS>(response, SimpleWeb::StatusCode::success_ok, xml);
  } else {
    logs::log(logs::warning, "[HTTPS] Received resume event from an unregistered session, ip: {}", client_ip);
  }

  server_error<SimpleWeb::HTTPS>(response);   // <-- also runs on the success path
}

send_xml() calls response->write(...) and sets close_connection_after_response = true (rest/helpers.hpp#L42-L50), and server_error() is itself a send_xml() with client_error_bad_request. So a successful resume emits 200 <resume xml> followed by 400 <error xml> on the same connection.

Three things suggest this is a leftover rather than intent:

  • launch() and cancel() both end with a single send_xml() and no trailing server_error().
  • The other three server_error<SimpleWeb::HTTPS>(response) calls in this file (L349, L356, L419) are all inside conditionals; this is the only one at function scope.
  • git blame puts the line in cb0e168e (2025-04-04) — the same commit that added the success-path send_xml() two lines above.

Impact

I want to be precise about what I have and have not checked:

  • Verified by reading the source: two responses are written on the success path.
  • Not verified at runtime: how any particular Moonlight client reacts. It may well ignore the trailing bytes, given the connection closes right after.

Independent of client tolerance, it makes a successful resume indistinguishable from a failed one at the protocol level, which is awkward precisely when debugging the resume path.

Suggested fix

Move the server_error() call inside the else. PR to follow.

Context

Found while investigating re-entry into an already-running session (#501, #504). I am not claiming this causes the CUDA-context crashes in those issues — it is a separate defect noticed along the way.

Environment

Read against stable @ 2273b2e. Running Wolf built from that commit on Debian 13, RTX 4090, NVIDIA driver 550.163.01 (proprietary).


Analysis assisted by Claude Code; the code references and git blame above were checked against the repository before filing.

Originally created by @jdonaldson on GitHub (Sep 21, 2026). Original GitHub issue: https://github.com/games-on-whales/wolf/issues/509 ## Summary `endpoints::https::resume()` writes **two** complete HTTP responses onto the same connection when a resume succeeds: the `200` + resume XML, immediately followed by a `400` from `server_error()`. The trailing `server_error()` call sits at function scope instead of inside the `else`. ## Code [`src/moonlight-server/rest/endpoints.hpp#L442-L474`](https://github.com/games-on-whales/wolf/blob/2273b2e64868e6a144daa225a85e85b216d3af2c/src/moonlight-server/rest/endpoints.hpp#L442-L474) (stable @ `2273b2e`): ```cpp if (old_session) { ... auto xml = moonlight::launch_resume(rtsp_ip, std::to_string(get_port(state::RTSP_SETUP_PORT))); send_xml<SimpleWeb::HTTPS>(response, SimpleWeb::StatusCode::success_ok, xml); } else { logs::log(logs::warning, "[HTTPS] Received resume event from an unregistered session, ip: {}", client_ip); } server_error<SimpleWeb::HTTPS>(response); // <-- also runs on the success path } ``` `send_xml()` calls `response->write(...)` and sets `close_connection_after_response = true` (`rest/helpers.hpp#L42-L50`), and `server_error()` is itself a `send_xml()` with `client_error_bad_request`. So a successful resume emits `200 <resume xml>` followed by `400 <error xml>` on the same connection. Three things suggest this is a leftover rather than intent: - `launch()` and `cancel()` both end with a single `send_xml()` and no trailing `server_error()`. - The other three `server_error<SimpleWeb::HTTPS>(response)` calls in this file (L349, L356, L419) are all inside conditionals; this is the only one at function scope. - `git blame` puts the line in `cb0e168e` (2025-04-04) — the same commit that added the success-path `send_xml()` two lines above. ## Impact I want to be precise about what I have and have not checked: - **Verified by reading the source:** two responses are written on the success path. - **Not verified at runtime:** how any particular Moonlight client reacts. It may well ignore the trailing bytes, given the connection closes right after. Independent of client tolerance, it makes a successful resume indistinguishable from a failed one at the protocol level, which is awkward precisely when debugging the resume path. ## Suggested fix Move the `server_error()` call inside the `else`. PR to follow. ## Context Found while investigating re-entry into an already-running session (#501, #504). I am not claiming this causes the CUDA-context crashes in those issues — it is a separate defect noticed along the way. ## Environment Read against `stable` @ `2273b2e`. Running Wolf built from that commit on Debian 13, RTX 4090, NVIDIA driver 550.163.01 (proprietary). --- <sub>Analysis assisted by Claude Code; the code references and `git blame` above were checked against the repository before filing.</sub>
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
games-on-whales/wolf#311
No description provided.