Moonraker: pass print=true in upload — fixes Upload & Print race with power-on-upload

Closes #14945.

Upload & Print on the Moonraker (Klipper) host type failed with
HTTP 503 "Klippy Host not connected" on any printer that Moonraker
powers up in response to an upload (the [power] on_when_upload_queued
feature). The file landed on disk, the print never started, and the
user hit an error dialog.

Root cause: after POST /server/files/upload succeeds we immediately
fire POST /printer/print/start. On a cold printer that Moonraker just
powered up, Klippy is still coming up when /printer/print/start
arrives, so Moonraker returns 503.

Fix: add `print=true` to the upload multipart form. Moonraker's own
upload endpoint queues the print inside the upload response — and
when a [power] device with on_when_upload_queued is configured, it
powers the printer on and waits for Klippy READY before starting.
That's the whole point of the power-on-upload feature; our second
POST was defeating it.

Also read `result.print_started` from the upload response — when
true, skip our explicit /printer/print/start (Moonraker handled it);
when false (older Moonraker or buddy-fork that ignores the print
flag), fall back to the explicit call so the existing behaviour is
preserved for those servers.

Reporter and root-cause identification: @RubenOllesch.
This commit is contained in:
packerlschupfer
2026-07-31 01:26:06 +02:00
parent 54dc5a2f1d
commit bd442155ba
+12 -9
View File
@@ -212,13 +212,10 @@ bool Moonraker::start_print(wxString &error_msg, const std::string &filename) co
bool Moonraker::upload(PrintHostUpload upload_data, ProgressFn progress_fn, ErrorFn error_fn, InfoFn info_fn) const
{
//ORCA: POST /server/files/upload as multipart/form-data with:
// file = <gcode file>
// root = <storage root> (Moonraker default: "gcodes")
// Successful response shape:
// { "result": { "item": { "path": "<name>.gcode", "root": "<root>" }, "print_started": <bool> } }
// We always start the print explicitly via /printer/print/start regardless of `print_started`
// so the user can rely on a single call site for state.
// POST /server/files/upload with `print=true` so Moonraker queues the print
// itself and respects [power] on_when_upload_queued (issue #14945). Older
// Moonrakers that ignore the flag return print_started=false and we fall
// back to the explicit /printer/print/start below.
wxString test_msg;
if (!test(test_msg)) {
error_fn(std::move(test_msg));
@@ -233,9 +230,12 @@ bool Moonraker::upload(PrintHostUpload upload_data, ProgressFn progress_fn, Erro
// addition later (storage picker) needs no change to this method.
const std::string root = upload_data.storage.empty() ? std::string("gcodes") : upload_data.storage;
const bool want_start = upload_data.post_action == PrintHostPostUploadAction::StartPrint;
std::string url = make_url("server/files/upload");
bool result = true;
std::string uploaded_path;
bool moonraker_started_print = false;
//ORCA: gcode inside a .gcode.3mf is index-coded (Metadata/plate_<N>.gcode), so the upload names the
// plate via a 1-based `plateindex` (set only in the .3mf path, see Plater::send_gcode_legacy);
@@ -249,13 +249,14 @@ bool Moonraker::upload(PrintHostUpload upload_data, ProgressFn progress_fn, Erro
% root
% upload_filename.string()
% (plateindex.empty() ? "-" : plateindex)
% (upload_data.post_action == PrintHostPostUploadAction::StartPrint ? "true" : "false");
% (want_start ? "true" : "false");
auto http = Http::post(std::move(url));
set_auth(http);
http.form_add("root", root);
if (!plateindex.empty())
http.form_add("plateindex", plateindex);
http.form_add("print", want_start ? "true" : "false");
http.form_add_file("file", upload_data.source_path.string(), upload_filename.string())
.on_complete([&](std::string body, unsigned status) {
BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: upload HTTP %2%: %3%") % name % status % body;
@@ -278,6 +279,7 @@ bool Moonraker::upload(PrintHostUpload upload_data, ProgressFn progress_fn, Erro
"%1%: upload response missing result.item.path, falling back to original filename `%2%`")
% name % uploaded_path;
}
moonraker_started_print = ptree.get<bool>("result.print_started", false);
} catch (const std::exception &ex) {
BOOST_LOG_TRIVIAL(warning) << boost::format(
"%1%: could not parse upload response (%2%); falling back to original filename")
@@ -306,7 +308,8 @@ bool Moonraker::upload(PrintHostUpload upload_data, ProgressFn progress_fn, Erro
if (!result)
return false;
if (upload_data.post_action == PrintHostPostUploadAction::StartPrint && !uploaded_path.empty()) {
// Fallback when Moonraker ignored the `print` flag or reported print_started=false.
if (want_start && !uploaded_path.empty() && !moonraker_started_print) {
wxString start_msg;
if (!start_print(start_msg, uploaded_path)) {
error_fn(std::move(start_msg));