Recreate a computer whose Docker network was removed (#226)
When a stack restart or network prune removes a bot's network, its stopped computer keeps pointing at the old network id. Starting it fails with "network ... not found", the supervisor returned 500 on every provision, and runs for that bot retried setup forever while staying queued. The supervisor now recognizes that error and recreates the container on a fresh network with the same home, as it already does for an outdated image. Claude-Session: https://claude.ai/code/session_01QAiXRe1FLMAJT9jkvwFZFq Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
parent
ea7892baa3
commit
679b90a667
@ -30,6 +30,7 @@ import {
|
||||
hasValidBearerToken,
|
||||
interactiveScreenCommand,
|
||||
isComputerControlUnavailable,
|
||||
isMissingNetworkError,
|
||||
nextScreenIndex,
|
||||
normalizeWorkspaceRelative,
|
||||
parseObservation,
|
||||
@ -846,3 +847,17 @@ describe("docker exec stream demux", () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("isMissingNetworkError", () => {
|
||||
it("recognizes Docker refusing to start a container whose network is gone", () => {
|
||||
expect(
|
||||
isMissingNetworkError(
|
||||
new Error(
|
||||
"(HTTP code 404) no such container - failed to set up container networking: network 065709cd6047a1b2 not found",
|
||||
),
|
||||
),
|
||||
).toBe(true);
|
||||
expect(isMissingNetworkError(new Error("(HTTP code 404) no such container"))).toBe(false);
|
||||
expect(isMissingNetworkError(new Error("image not found"))).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
@ -71,6 +71,7 @@ import {
|
||||
hasValidBearerToken,
|
||||
interactiveScreenCommand,
|
||||
isComputerControlUnavailable,
|
||||
isMissingNetworkError,
|
||||
nextScreenIndex,
|
||||
normalizeWorkspaceRelative,
|
||||
parseObservation,
|
||||
@ -287,12 +288,24 @@ app.post("/computers", async (c) => {
|
||||
(!brokerSocketDir || githubBrokerSocketMounted(info.Mounts)) &&
|
||||
extraBindsMounted(info.Mounts, resolveExtraBinds())
|
||||
) {
|
||||
if (!info.State.Running) await existing.start();
|
||||
return c.json({
|
||||
id: existing.id,
|
||||
image: COMPUTER_IMAGE,
|
||||
resumed: true,
|
||||
});
|
||||
// A container whose network was removed (a stack restart or network prune)
|
||||
// never starts again; recreate it below on a fresh network with the same home.
|
||||
const running =
|
||||
info.State.Running ||
|
||||
(await existing.start().then(
|
||||
() => true,
|
||||
(error: unknown) => {
|
||||
if (isMissingNetworkError(error)) return false;
|
||||
throw error;
|
||||
},
|
||||
));
|
||||
if (running) {
|
||||
return c.json({
|
||||
id: existing.id,
|
||||
image: COMPUTER_IMAGE,
|
||||
resumed: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -83,6 +83,11 @@ export async function preferComputerControl<T>(
|
||||
}
|
||||
}
|
||||
|
||||
/** Docker cannot start a container whose network was removed underneath it. */
|
||||
export function isMissingNetworkError(error: unknown) {
|
||||
return /network \S+ not found/i.test(error instanceof Error ? error.message : String(error));
|
||||
}
|
||||
|
||||
export class ComputerControlUnavailableError extends Error {
|
||||
constructor(message = "computer control unavailable") {
|
||||
super(message);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user