mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 16:40:49 +00:00
fix: harden startup readiness and discord replies
(cherry picked from commit 3956672106b3387d42427a485a9ca01e77f3b78f)
This commit is contained in:
committed by
Peter Steinberger
parent
7e229f0d3d
commit
e259938e96
@@ -118,7 +118,11 @@ export async function withOperatorApprovalsGatewayClient<T>(
|
||||
clientOptions: { preauthHandshakeTimeoutMs: params.config.gateway?.handshakeTimeoutMs },
|
||||
});
|
||||
if (!readiness.ready) {
|
||||
throw new Error("gateway event loop readiness timeout");
|
||||
throw new Error(
|
||||
readiness.aborted
|
||||
? "gateway approval client start aborted before readiness"
|
||||
: "gateway readiness unavailable before approval client start",
|
||||
);
|
||||
}
|
||||
await ready;
|
||||
return await run(gatewayClient);
|
||||
|
||||
@@ -2,6 +2,7 @@ export const STARTUP_UNAVAILABLE_GATEWAY_METHODS = [
|
||||
"agent.wait",
|
||||
"chat.history",
|
||||
"models.list",
|
||||
"sessions.list",
|
||||
"sessions.abort",
|
||||
"sessions.create",
|
||||
"sessions.send",
|
||||
|
||||
@@ -232,6 +232,55 @@ describe("startChannelApprovalHandlerBootstrap", () => {
|
||||
await cleanup();
|
||||
});
|
||||
|
||||
it("defers retryable gateway readiness startup failures without terminal error logs", async () => {
|
||||
vi.useFakeTimers();
|
||||
const channelRuntime = createRuntimeChannel();
|
||||
const readinessError = new Error("gateway event loop readiness timeout");
|
||||
const start = vi.fn().mockRejectedValueOnce(readinessError).mockResolvedValueOnce(undefined);
|
||||
const stop = vi.fn().mockResolvedValue(undefined);
|
||||
const logger = {
|
||||
error: vi.fn(),
|
||||
warn: vi.fn(),
|
||||
info: vi.fn(),
|
||||
debug: vi.fn(),
|
||||
child: vi.fn(),
|
||||
isEnabled: vi.fn().mockReturnValue(true),
|
||||
isVerboseEnabled: vi.fn().mockReturnValue(false),
|
||||
verbose: vi.fn(),
|
||||
};
|
||||
createChannelApprovalHandlerFromCapability
|
||||
.mockResolvedValueOnce({ start, stop })
|
||||
.mockResolvedValueOnce({ start, stop });
|
||||
|
||||
const cleanup = await startTestBootstrap({ channelRuntime, logger });
|
||||
|
||||
registerApprovalContext(channelRuntime);
|
||||
await flushTransitions();
|
||||
|
||||
expect(start).toHaveBeenCalledTimes(1);
|
||||
await flushTransitions();
|
||||
expect(logger.error).not.toHaveBeenCalledWith(
|
||||
expect.stringContaining("failed to start native approval handler"),
|
||||
);
|
||||
expect(logger.warn).toHaveBeenCalledWith(
|
||||
expect.stringContaining("native approval handler deferred until gateway readiness recovers"),
|
||||
);
|
||||
expect(logger.warn).toHaveBeenCalledWith(
|
||||
expect.stringContaining("gateway readiness unavailable before approval handler start"),
|
||||
);
|
||||
expect(logger.warn).not.toHaveBeenCalledWith(
|
||||
expect.stringContaining("gateway event loop readiness timeout"),
|
||||
);
|
||||
|
||||
await vi.advanceTimersByTimeAsync(1_000);
|
||||
await flushTransitions();
|
||||
|
||||
expect(createChannelApprovalHandlerFromCapability).toHaveBeenCalledTimes(2);
|
||||
expect(start).toHaveBeenCalledTimes(2);
|
||||
|
||||
await cleanup();
|
||||
});
|
||||
|
||||
it("does not retry terminal native approval startup failures", async () => {
|
||||
vi.useFakeTimers();
|
||||
const channelRuntime = createRuntimeChannel();
|
||||
|
||||
@@ -17,6 +17,28 @@ import { isExecApprovalChannelRuntimeTerminalStartError } from "./exec-approval-
|
||||
type ApprovalBootstrapHandler = ChannelApprovalHandler;
|
||||
const APPROVAL_HANDLER_BOOTSTRAP_RETRY_MS = 1_000;
|
||||
|
||||
function isRetryableApprovalBootstrapStartError(error: unknown): boolean {
|
||||
const message = String(error);
|
||||
return (
|
||||
message.includes("gateway readiness unavailable before approval client start") ||
|
||||
message.includes("gateway approval client start aborted before readiness") ||
|
||||
message.includes("gateway readiness unavailable before exec approval runtime start") ||
|
||||
message.includes("gateway approval runtime start aborted before readiness") ||
|
||||
message.includes("gateway event loop readiness timeout") ||
|
||||
message.includes("gateway starting") ||
|
||||
message.includes("code=1013") ||
|
||||
message.includes("close code 1013")
|
||||
);
|
||||
}
|
||||
|
||||
function formatRetryableApprovalBootstrapStartError(error: unknown): string {
|
||||
const message = String(error);
|
||||
if (message.includes("gateway event loop readiness timeout")) {
|
||||
return "gateway readiness unavailable before approval handler start";
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
export async function startChannelApprovalHandlerBootstrap(params: {
|
||||
plugin: Pick<ChannelPlugin, "id" | "meta" | "approvalCapability">;
|
||||
cfg: OpenClawConfig;
|
||||
@@ -122,6 +144,13 @@ export async function startChannelApprovalHandlerBootstrap(params: {
|
||||
logger.error(`native approval handler disabled: ${String(error)}`);
|
||||
return;
|
||||
}
|
||||
if (isRetryableApprovalBootstrapStartError(error)) {
|
||||
logger.warn(
|
||||
`native approval handler deferred until gateway readiness recovers: ${formatRetryableApprovalBootstrapStartError(error)}`,
|
||||
);
|
||||
scheduleRetryForContext(context, generation);
|
||||
return;
|
||||
}
|
||||
logger.error(`failed to start native approval handler: ${String(error)}`);
|
||||
scheduleRetryForContext(context, generation);
|
||||
}
|
||||
|
||||
@@ -291,7 +291,9 @@ describe("createExecApprovalChannelRuntime", () => {
|
||||
finalizeResolved: async () => undefined,
|
||||
});
|
||||
|
||||
await expect(runtime.start()).rejects.toThrow("gateway event loop readiness timeout");
|
||||
await expect(runtime.start()).rejects.toThrow(
|
||||
"gateway readiness unavailable before exec approval runtime start",
|
||||
);
|
||||
|
||||
expect(mockGatewayClientStarts).not.toHaveBeenCalled();
|
||||
expect(mockGatewayClientStops).toHaveBeenCalledTimes(1);
|
||||
|
||||
@@ -365,7 +365,11 @@ export function createExecApprovalChannelRuntime<
|
||||
},
|
||||
});
|
||||
if (!readiness.ready) {
|
||||
throw new Error("gateway event loop readiness timeout");
|
||||
throw new Error(
|
||||
readiness.aborted
|
||||
? "gateway approval runtime start aborted before readiness"
|
||||
: "gateway readiness unavailable before exec approval runtime start",
|
||||
);
|
||||
}
|
||||
await ready;
|
||||
if (stopClientIfInactive(client)) {
|
||||
|
||||
Reference in New Issue
Block a user