mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-16 04:20:44 +00:00
fix(update): fallback to --omit=optional when global npm update fails (#24896)
* fix(update): fallback to --omit=optional when global npm update fails * fix(update): add recovery hints and fallback for npm global update failures * chore(update): align fallback progress step index ordering * chore(update): label omit-optional retry step in progress output * chore(update): avoid showing 1/2 when fallback path is not used * chore(ci): retrigger after unrelated test OOM * fix(update): scope recovery hints to npm failures * test(update): cover non-npm hint suppression --------- Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
This commit is contained in:
@@ -28,6 +28,7 @@ const STEP_LABELS: Record<string, string> = {
|
||||
"openclaw doctor": "Running doctor checks",
|
||||
"git rev-parse HEAD (after)": "Verifying update",
|
||||
"global update": "Updating via package manager",
|
||||
"global update (omit optional)": "Retrying update without optional deps",
|
||||
"global install": "Installing global package",
|
||||
};
|
||||
|
||||
@@ -35,6 +36,40 @@ function getStepLabel(step: UpdateStepInfo): string {
|
||||
return STEP_LABELS[step.name] ?? step.name;
|
||||
}
|
||||
|
||||
export function inferUpdateFailureHints(result: UpdateRunResult): string[] {
|
||||
if (result.status !== "error" || result.mode !== "npm") {
|
||||
return [];
|
||||
}
|
||||
const failedStep = [...result.steps].toReversed().find((step) => step.exitCode !== 0);
|
||||
if (!failedStep) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const stderr = (failedStep.stderrTail ?? "").toLowerCase();
|
||||
const hints: string[] = [];
|
||||
|
||||
if (failedStep.name.startsWith("global update") && stderr.includes("eacces")) {
|
||||
hints.push(
|
||||
"Detected permission failure (EACCES). Re-run with a writable global prefix or sudo (for system-managed Node installs).",
|
||||
);
|
||||
hints.push("Example: npm config set prefix ~/.local && npm i -g openclaw@latest");
|
||||
}
|
||||
|
||||
if (
|
||||
failedStep.name.startsWith("global update") &&
|
||||
(stderr.includes("node-gyp") ||
|
||||
stderr.includes("@discordjs/opus") ||
|
||||
stderr.includes("prebuild"))
|
||||
) {
|
||||
hints.push(
|
||||
"Detected native optional dependency build failure (e.g. opus). The updater retries with --omit=optional automatically.",
|
||||
);
|
||||
hints.push("If it still fails: npm i -g openclaw@latest --omit=optional");
|
||||
}
|
||||
|
||||
return hints;
|
||||
}
|
||||
|
||||
export type ProgressController = {
|
||||
progress: UpdateStepProgress;
|
||||
stop: () => void;
|
||||
@@ -151,6 +186,15 @@ export function printResult(result: UpdateRunResult, opts: PrintResultOptions):
|
||||
}
|
||||
}
|
||||
|
||||
const hints = inferUpdateFailureHints(result);
|
||||
if (hints.length > 0) {
|
||||
defaultRuntime.log("");
|
||||
defaultRuntime.log(theme.heading("Recovery hints:"));
|
||||
for (const hint of hints) {
|
||||
defaultRuntime.log(` - ${theme.warn(hint)}`);
|
||||
}
|
||||
}
|
||||
|
||||
defaultRuntime.log("");
|
||||
defaultRuntime.log(`Total time: ${theme.muted(formatDurationPrecise(result.durationMs))}`);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user