From d9cc468478351f8eb264bb8014ac73978f6f9160 Mon Sep 17 00:00:00 2001 From: Roman Priesol Date: Wed, 9 Sep 2026 18:17:24 +0200 Subject: [PATCH] Show the actual plugin run error instead of a generic message MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A failed run showed "Failed — see server log" regardless of why — misleading, since a Plugins::RunError never actually reaches any log (app.rb turns it straight into a 502 JSON response). Found this the hard way debugging the p4n2gpx plugin, having to SSH in and reproduce the run by hand each time just to see why it failed. Now shows the response's actual error message (e.g. "plugin exited with status 1"), wrapping instead of overflowing the flyout for a longer one. --- public/css/style.css | 9 +++++++++ public/js/plugins.js | 19 +++++++++++++++---- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/public/css/style.css b/public/css/style.css index 13246a3..4b60119 100644 --- a/public/css/style.css +++ b/public/css/style.css @@ -1487,8 +1487,17 @@ html, body { .plugins-panel__status { min-width: 3.5em; + max-width: 200px; color: #888; font-size: 11px; + /* Overrides the parent's nowrap: "Running…"/"Done" never wrap either + way, but a real RunError message (e.g. "plugin exited with status 1") + needs to, or it just overflows the flyout unreadably. */ + white-space: normal; +} + +.plugins-panel__status--error { + color: #c62828; } .plugins-panel__empty { diff --git a/public/js/plugins.js b/public/js/plugins.js index a43ab74..2c6a983 100644 --- a/public/js/plugins.js +++ b/public/js/plugins.js @@ -83,12 +83,23 @@ export class PluginsControl { east: bounds.getEast(), }); const response = await fetch(`/api/plugins/${encodeURIComponent(plugin.name)}/run`, { method: "POST", body }); - if (!response.ok) throw new Error(`run failed: ${response.status}`); - const { path } = await response.json(); - notifyGpxStoreChanged(path); + // Every app.rb response on this route is JSON, success or error — + // the catch below is only for an infra-level failure in front of it + // (a proxy's own HTML error page, a dropped connection) that never + // reached app.rb at all. + const data = await response.json().catch(() => ({})); + if (!response.ok) throw new Error(data.error || `run failed: ${response.status}`); + notifyGpxStoreChanged(data.path); status.textContent = "Done"; + status.classList.remove("plugins-panel__status--error"); } catch (err) { - status.textContent = "Failed — see server log"; + // Plugins::RunError's message (e.g. "plugin exited with status 1") + // reaches here as `data.error` above — nothing about a plugin run + // failure gets logged server-side (app.rb turns it into a plain 502 + // JSON response, not an exception), so showing that message directly + // is the only way to see why, short of SSHing in to reproduce it. + status.textContent = err.message; + status.classList.add("plugins-panel__status--error"); } finally { button.disabled = false; }