diff options
| author | SulfurNitride <SulfurNitride@users.noreply.github.com> | 2026-04-15 16:57:40 -0500 |
|---|---|---|
| committer | SulfurNitride <SulfurNitride@users.noreply.github.com> | 2026-04-15 16:57:40 -0500 |
| commit | 7a865a8b757dc8aa5b521fce2d610fa52a86a9f1 (patch) | |
| tree | 0fd1e2e7c438b22b154b4d235137c86d86ea5155 | |
| parent | 4cd6aa34160ec97180b41b9658f2ab31264ce1ae (diff) | |
Abort setup on prefix-init failure, flag broken Proton installs
When stepProtonInit failed, the runner kept marching through every
downstream step against a half-initialized prefix, producing a cascade
of misleading "wine client error: version mismatch" failures that
masked the real problem. Seen in the wild with a GE-Proton10-34 install
that was missing concrt140.dll from its bundled default_pfx template:
setup_prefix crashes with FileNotFoundError, then seven unrelated steps
are reported as broken.
Break out of the step loop as soon as proton_init fails, and inspect the
captured Proton output for the "FileNotFoundError" + "default_pfx" hint
so the error message points at the broken Proton distribution instead
of a generic "exit code 1". runProcess gains an optional QByteArray*
out param so the caller can pattern-match on the merged output without
changing any existing call sites.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
| -rw-r--r-- | src/src/prefixsetuprunner.cpp | 52 | ||||
| -rw-r--r-- | src/src/prefixsetuprunner.h | 8 |
2 files changed, 51 insertions, 9 deletions
diff --git a/src/src/prefixsetuprunner.cpp b/src/src/prefixsetuprunner.cpp index 74d59bc..bc1b9ce 100644 --- a/src/src/prefixsetuprunner.cpp +++ b/src/src/prefixsetuprunner.cpp @@ -484,8 +484,20 @@ void PrefixSetupRunner::start() if (m_steps[i].status == SetupStep::Succeeded) continue; - allOk = runStep(i) && allOk; + const bool stepOk = runStep(i); + allOk = stepOk && allOk; emit progressChanged(static_cast<float>(i + 1) / total); + + // The prefix-init step is a hard prerequisite for everything that + // follows. If it fails (e.g. broken Proton install), running downstream + // steps just produces a cascade of misleading "version mismatch" errors + // against a half-initialized prefix. Surface the real error and stop. + if (!stepOk && m_steps[i].id == "proton_init") { + emit logMessage( + "Prefix initialization failed — skipping remaining setup steps. " + "Fix the Proton installation and retry."); + break; + } } emit finished(allOk); @@ -625,7 +637,8 @@ QProcess* PrefixSetupRunner::buildWrappedProcess( int PrefixSetupRunner::runProcess(const QString& exe, const QStringList& args, const QMap<QString, QString>& extraEnv, - int timeoutMs) + int timeoutMs, + QByteArray* captured) { QProcess* proc = buildWrappedProcess(exe, extraEnv); @@ -643,8 +656,10 @@ int PrefixSetupRunner::runProcess(const QString& exe, if (proc->canReadLine() || proc->bytesAvailable() > 0) { const QByteArray data = proc->readAll(); - if (!data.isEmpty()) + if (!data.isEmpty()) { + if (captured) captured->append(data); emitFilteredOutput(this, data); + } } if (isCancelled()) { @@ -656,8 +671,10 @@ int PrefixSetupRunner::runProcess(const QString& exe, } const QByteArray remaining = proc->readAll(); - if (!remaining.isEmpty()) + if (!remaining.isEmpty()) { + if (captured) captured->append(remaining); emitFilteredOutput(this, remaining); + } const int exitCode = proc->exitCode(); proc->deleteLater(); @@ -785,12 +802,33 @@ bool PrefixSetupRunner::stepProtonInit() emit logMessage("Initializing Wine prefix with Proton..."); + QByteArray protonOutput; const int rc = runProcess(protonScript, {"run", "wineboot", "-u"}, - env); + env, -1, &protonOutput); if (rc != 0) { - m_steps.last().errorMessage = - QStringLiteral("proton wineboot failed (exit code %1)").arg(rc); + // Detect a broken Proton install: setup_prefix copies DLLs from Proton's + // bundled default_pfx template, so a FileNotFoundError there means the + // Proton distribution itself is missing files — not a Fluorine issue. + const QByteArray out = protonOutput; + if (out.contains("FileNotFoundError") && + out.contains("default_pfx/drive_c")) { + int start = out.indexOf("default_pfx/drive_c"); + int end = out.indexOf('\'', start); + if (end < 0) end = out.indexOf('"', start); + const QString missing = + (end > start) ? QString::fromUtf8(out.mid(start, end - start)) + : QStringLiteral("default_pfx/drive_c/..."); + m_steps.last().errorMessage = + QStringLiteral( + "Proton install is incomplete: missing '%1'. " + "Reinstall or verify your Proton build (e.g. GE-Proton) — " + "this is not a Fluorine bug.") + .arg(missing); + } else { + m_steps.last().errorMessage = + QStringLiteral("proton wineboot failed (exit code %1)").arg(rc); + } return false; } diff --git a/src/src/prefixsetuprunner.h b/src/src/prefixsetuprunner.h index ed8f12c..5ac71c8 100644 --- a/src/src/prefixsetuprunner.h +++ b/src/src/prefixsetuprunner.h @@ -2,6 +2,7 @@ #define PREFIXSETUPRUNNER_H #include <QAtomicInt> +#include <QByteArray> #include <QObject> #include <QProcess> #include <QString> @@ -63,11 +64,14 @@ private: bool isCancelled() const { return m_cancelled.loadAcquire() != 0; } /// Run an external process with SLR wrapping and log its output. - /// Returns exit code (0 = success). + /// Returns exit code (0 = success). If captured is non-null, a copy of + /// the merged stdout/stderr output is appended to it for post-mortem + /// pattern matching. int runProcess(const QString& exe, const QStringList& args, const QMap<QString, QString>& extraEnv, - int timeoutMs = -1); + int timeoutMs = -1, + QByteArray* captured = nullptr); /// Run a plain host command (NO SLR wrapping). /// Used for host utilities like curl, unzip that must not run in the container. |
