mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 05:10:44 +00:00
fix: clarify slack socket retry errors
This commit is contained in:
@@ -87,6 +87,7 @@ Docs: https://docs.openclaw.ai
|
||||
- active-memory: skip the memory sub-agent gracefully instead of logging a confusing allowlist error when no memory plugin (`memory-core` or `memory-lancedb`) is loaded, so active-memory with no memory backend no longer produces misleading "No callable tools remain" warnings in the gateway log. Fixes #77506. Thanks @hclsys.
|
||||
- Memory/wiki: preserve representation from both corpora in `corpus=all` searches while backfilling unused result capacity, so memory hits are not starved by numerically higher wiki integer scores. Fixes #77337. Thanks @hclsys.
|
||||
- Telegram: clean up tool-only draft previews after assistant message boundaries so transient `Surfacing...` tool-status bubbles do not linger when no matching final preview arrives. Thanks @BunsDev.
|
||||
- Slack: report `unknown error` instead of `undefined` in socket-mode startup retry logs and label the retry reason explicitly.
|
||||
- Telegram: let explicit forum-topic `requireMention` settings override persisted `/activate` and `/deactivate` state, so per-topic mention gates work consistently. Fixes #49864. Thanks @Panniantong.
|
||||
- Cron: surface failed isolated-run diagnostics in `cron show`, status, and run history when requested tools are unavailable, so blocked cron runs report the actual tool-policy failure instead of a misleading green result. Fixes #75763. Thanks @RyanSandoval.
|
||||
- TUI/escape abort: track the in-flight runId after `chat.send` resolves so pressing Esc during the gap before the first gateway event aborts the run instead of repeatedly printing `no active run`. Fixes #1296. Thanks @Lukavyi and @romneyda.
|
||||
|
||||
@@ -5,8 +5,11 @@ import {
|
||||
publishSlackDisconnectedStatus,
|
||||
startSlackSocketAndWaitForDisconnect,
|
||||
} from "./provider-support.js";
|
||||
import { formatSlackSocketReconnectMessage } from "./provider.js";
|
||||
import { waitForSlackSocketDisconnect } from "./reconnect-policy.js";
|
||||
import {
|
||||
formatSlackSocketReconnectMessage,
|
||||
formatSlackSocketStartRetryMessage,
|
||||
} from "./provider.js";
|
||||
import { formatUnknownError, waitForSlackSocketDisconnect } from "./reconnect-policy.js";
|
||||
|
||||
class FakeEmitter {
|
||||
private listeners = new Map<string, Set<(...args: unknown[]) => void>>();
|
||||
@@ -97,6 +100,28 @@ describe("slack socket reconnect helpers", () => {
|
||||
).toBe("slack socket disconnected (disconnect); reconnecting in 2s (attempt 1/12)");
|
||||
});
|
||||
|
||||
it("formats missing and unserializable socket errors without leaking undefined", () => {
|
||||
const circular: Record<string, unknown> = {};
|
||||
circular.self = circular;
|
||||
|
||||
expect(formatUnknownError(undefined)).toBe("unknown error");
|
||||
expect(formatUnknownError(null)).toBe("unknown error");
|
||||
expect(formatUnknownError("")).toBe("unknown error");
|
||||
expect(formatUnknownError(new Error(""))).toBe("Error");
|
||||
expect(formatUnknownError(circular)).toBe("unknown error");
|
||||
});
|
||||
|
||||
it("formats socket start retries with an explicit reason field", () => {
|
||||
expect(
|
||||
formatSlackSocketStartRetryMessage({
|
||||
attempt: 1,
|
||||
maxAttempts: 12,
|
||||
delayMs: 2_340,
|
||||
error: undefined,
|
||||
}),
|
||||
).toBe('slack socket mode failed to start; retry 1/12 in 2s reason="unknown error"');
|
||||
});
|
||||
|
||||
it("resolves disconnect waiter on socket disconnect event", async () => {
|
||||
const client = new FakeEmitter();
|
||||
const app = { receiver: { client } };
|
||||
|
||||
@@ -97,6 +97,16 @@ export function formatSlackSocketReconnectMessage(params: {
|
||||
return `slack socket disconnected (${params.event}); reconnecting in ${Math.round(params.delayMs / 1000)}s (attempt ${params.attempt}/${maxAttempts})${suffix}`;
|
||||
}
|
||||
|
||||
export function formatSlackSocketStartRetryMessage(params: {
|
||||
attempt: number;
|
||||
maxAttempts: number;
|
||||
delayMs: number;
|
||||
error: unknown;
|
||||
}) {
|
||||
const maxAttempts = params.maxAttempts > 0 ? String(params.maxAttempts) : "∞";
|
||||
return `slack socket mode failed to start; retry ${params.attempt}/${maxAttempts} in ${Math.round(params.delayMs / 1000)}s reason="${formatUnknownError(params.error)}"`;
|
||||
}
|
||||
|
||||
function parseApiAppIdFromAppToken(raw?: string) {
|
||||
const token = raw?.trim();
|
||||
if (!token) {
|
||||
@@ -534,7 +544,12 @@ export async function monitorSlackProvider(opts: MonitorSlackOpts = {}) {
|
||||
}
|
||||
const delayMs = computeBackoff(SLACK_SOCKET_RECONNECT_POLICY, reconnectAttempts);
|
||||
runtime.error?.(
|
||||
`slack socket mode failed to start. retry ${reconnectAttempts}/${SLACK_SOCKET_RECONNECT_POLICY.maxAttempts || "∞"} in ${Math.round(delayMs / 1000)}s (${formatUnknownError(err)})`,
|
||||
formatSlackSocketStartRetryMessage({
|
||||
attempt: reconnectAttempts,
|
||||
maxAttempts: SLACK_SOCKET_RECONNECT_POLICY.maxAttempts,
|
||||
delayMs,
|
||||
error: err,
|
||||
}),
|
||||
);
|
||||
try {
|
||||
await sleepWithAbort(delayMs, opts.abortSignal);
|
||||
|
||||
@@ -94,14 +94,17 @@ export function isNonRecoverableSlackAuthError(error: unknown): boolean {
|
||||
}
|
||||
|
||||
export function formatUnknownError(error: unknown): string {
|
||||
if (error === undefined || error === null) {
|
||||
return "unknown error";
|
||||
}
|
||||
if (error instanceof Error) {
|
||||
return error.message;
|
||||
return error.message || error.name || "unknown error";
|
||||
}
|
||||
if (typeof error === "string") {
|
||||
return error;
|
||||
return error || "unknown error";
|
||||
}
|
||||
try {
|
||||
return JSON.stringify(error);
|
||||
return JSON.stringify(error) ?? "unknown error";
|
||||
} catch {
|
||||
return "unknown error";
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user