Recover SE updates from stuck state 0x6 by removing appmanifest and retrying
All checks were successful
Deploy GSM / deploy (push) Successful in 22s

A validate pass alone does not unstick SteamCMD when it lands in state 0x6
with BytesToDownload=0 and a TargetBuildID newer than buildid: Steam never
schedules a depot fetch. Verified manually that deleting appmanifest_298740.acf
forces a clean reconciliation and a full re-validate that actually downloads
the missing depot. The handler now retries with the manifest removed when the
first pass leaves a pending target.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-10 20:18:04 +02:00
parent 71e77f7842
commit 33011056c2

View File

@@ -485,11 +485,9 @@ const updateHandlers = {
const oldMatch = (oldManifest.stdout || "").match(/"buildid"\s*"([^"]+)"/);
const oldBuildId = oldMatch ? oldMatch[1] : null;
console.log("[Update] Starting Space Engineers SteamCMD update (one-shot container)...");
// One-shot container: same image + same volumes, but our own steamcmd call
// with force_install_dir BEFORE login (the upstream entrypoint has them swapped)
const updateCmd = [
const buildUpdateCmd = () => [
`docker run --rm`,
`--entrypoint /bin/bash`,
`-v ${installHostDir}:/appdata/space-engineers/SpaceEngineersDedicated`,
@@ -498,26 +496,46 @@ const updateHandlers = {
`-c "runuser -l wine bash -c 'steamcmd +@sSteamCmdForcePlatformType windows +force_install_dir /appdata/space-engineers/SpaceEngineersDedicated +login anonymous +app_update ${appId} validate +quit'"`
].join(" ");
const updateResult = await ssh.execCommand(updateCmd, { execOptions: { timeout: 1200000 } }); // 20 min
console.log("[Update] SteamCMD output (tail):", updateResult.stdout.slice(-2000));
const readManifest = async () => {
const r = await ssh.execCommand(`cat ${manifestPath} 2>/dev/null`);
const text = r.stdout || "";
return {
buildid: text.match(/"buildid"\s*"([^"]+)"/)?.[1] || null,
target: text.match(/"TargetBuildID"\s*"([^"]+)"/)?.[1] || null
};
};
// Verify success by re-reading appmanifest: buildid should now match TargetBuildID (or have changed)
const newManifest = await ssh.execCommand(`cat ${manifestPath} 2>/dev/null`);
const newMatch = (newManifest.stdout || "").match(/"buildid"\s*"([^"]+)"/);
const newTargetMatch = (newManifest.stdout || "").match(/"TargetBuildID"\s*"([^"]+)"/);
const newBuildId = newMatch ? newMatch[1] : null;
const newTargetBuildId = newTargetMatch ? newTargetMatch[1] : null;
console.log("[Update] Starting Space Engineers SteamCMD update (validate pass)...");
let updateResult = await ssh.execCommand(buildUpdateCmd(), { execOptions: { timeout: 1200000 } });
console.log("[Update] SteamCMD output (tail):", updateResult.stdout.slice(-1500));
const stillPending = newTargetBuildId && newTargetBuildId !== "0" && newTargetBuildId !== newBuildId;
const buildChanged = oldBuildId && newBuildId && oldBuildId !== newBuildId;
const out = updateResult.stdout || "";
const reportedSuccess = out.includes("Success! App '298740'") || out.includes("fully installed");
let manifest = await readManifest();
let stillPending = manifest.target && manifest.target !== "0" && manifest.target !== manifest.buildid;
let buildChanged = oldBuildId && manifest.buildid && oldBuildId !== manifest.buildid;
let reportedSuccess = (updateResult.stdout || "").includes("fully installed");
// Recovery: SteamCMD sometimes lands in state 0x6 with no bytes pending despite
// a TargetBuildID newer than buildid. validate alone won't dislodge it; deleting
// the appmanifest forces a clean reconciliation on the next run.
if (!buildChanged && !reportedSuccess && stillPending) {
console.log("[Update] First pass left a stuck pending update. Removing appmanifest and retrying...");
await ssh.execCommand(`rm -f ${manifestPath}`);
updateResult = await ssh.execCommand(buildUpdateCmd(), { execOptions: { timeout: 1800000 } }); // 30 min for full reinstall
console.log("[Update] SteamCMD output (retry tail):", updateResult.stdout.slice(-1500));
manifest = await readManifest();
buildChanged = oldBuildId && manifest.buildid && oldBuildId !== manifest.buildid;
reportedSuccess = (updateResult.stdout || "").includes("fully installed");
stillPending = manifest.target && manifest.target !== "0" && manifest.target !== manifest.buildid;
}
if (!buildChanged && !reportedSuccess && stillPending) {
const errLine = out.match(/Error![^\n]*/)?.[0] || updateResult.stderr || "SteamCMD meldet noch ausstehendes Update";
const errLine = (updateResult.stdout || "").match(/Error![^\n]*/)?.[0] || updateResult.stderr || "SteamCMD meldet noch ausstehendes Update";
throw new Error("Update fehlgeschlagen: " + errLine);
}
const newBuildId = manifest.buildid;
return {
success: true,
message: "Update erfolgreich! Server kann jetzt gestartet werden.",