From 626a1d0699f2174514627135e53ed08e11bb996d Mon Sep 17 00:00:00 2001 From: 0xRain Date: Fri, 13 Feb 2026 00:48:49 +0800 Subject: [PATCH] fix(gateway): increase WebSocket max payload to 5 MB for image uploads (#14486) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(gateway): increase WebSocket max payload to 5 MB for image uploads The 512 KB limit was too small for base64-encoded images — a 400 KB image becomes ~532 KB after encoding, exceeding the limit and closing the connection with code 1006. Bump MAX_PAYLOAD_BYTES to 5 MB and MAX_BUFFERED_BYTES to 8 MB to support standard image uploads via webchat. Closes #14400 * fix: align gateway WS limits with 5MB image uploads (#14486) (thanks @0xRaini) * docs: fix changelog conflict for #14486 --------- Co-authored-by: 0xRaini <0xRaini@users.noreply.github.com> Co-authored-by: Peter Steinberger --- CHANGELOG.md | 1 + src/gateway/chat-attachments.ts | 2 +- src/gateway/server-constants.ts | 4 ++-- .../monitor/message-handler/prepare.sender-prefix.test.ts | 4 ++-- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ea2c7dc9ca..c8cc1216528 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ Docs: https://docs.openclaw.ai ### Fixes +- Gateway: raise WS payload/buffer limits so 5,000,000-byte image attachments work reliably. (#14486) Thanks @0xRaini. - Cron: use requested `agentId` for isolated job auth resolution. (#13983) Thanks @0xRaini. - Cron: prevent cron jobs from skipping execution when `nextRunAtMs` advances. (#14068) Thanks @WalterSumbon. - Cron: pass `agentId` to `runHeartbeatOnce` for main-session jobs. (#14140) Thanks @ishikawa-pro. diff --git a/src/gateway/chat-attachments.ts b/src/gateway/chat-attachments.ts index 0ff3181ee8a..4a4308b57ae 100644 --- a/src/gateway/chat-attachments.ts +++ b/src/gateway/chat-attachments.ts @@ -64,7 +64,7 @@ export async function parseMessageWithAttachments( attachments: ChatAttachment[] | undefined, opts?: { maxBytes?: number; log?: AttachmentLog }, ): Promise { - const maxBytes = opts?.maxBytes ?? 5_000_000; // 5 MB + const maxBytes = opts?.maxBytes ?? 5_000_000; // decoded bytes (5,000,000) const log = opts?.log; if (!attachments || attachments.length === 0) { return { message, images: [] }; diff --git a/src/gateway/server-constants.ts b/src/gateway/server-constants.ts index 996ea63585b..03107331fed 100644 --- a/src/gateway/server-constants.ts +++ b/src/gateway/server-constants.ts @@ -1,5 +1,5 @@ -export const MAX_PAYLOAD_BYTES = 512 * 1024; // cap incoming frame size -export const MAX_BUFFERED_BYTES = 1.5 * 1024 * 1024; // per-connection send buffer limit +export const MAX_PAYLOAD_BYTES = 8 * 1024 * 1024; // cap incoming frame size (~8 MiB; fits ~5,000,000 decoded bytes as base64 + JSON overhead) +export const MAX_BUFFERED_BYTES = 16 * 1024 * 1024; // per-connection send buffer limit (2x max payload) const DEFAULT_MAX_CHAT_HISTORY_MESSAGES_BYTES = 6 * 1024 * 1024; // keep history responses comfortably under client WS limits let maxChatHistoryMessagesBytes = DEFAULT_MAX_CHAT_HISTORY_MESSAGES_BYTES; diff --git a/src/slack/monitor/message-handler/prepare.sender-prefix.test.ts b/src/slack/monitor/message-handler/prepare.sender-prefix.test.ts index 8f8c7a3386b..30cfdc1ef9d 100644 --- a/src/slack/monitor/message-handler/prepare.sender-prefix.test.ts +++ b/src/slack/monitor/message-handler/prepare.sender-prefix.test.ts @@ -44,7 +44,7 @@ describe("prepareSlackMessage sender prefix", () => { ackReactionScope: "off", mediaMaxBytes: 1000, removeAckAfterReply: false, - logger: { info: vi.fn() }, + logger: { info: vi.fn(), warn: vi.fn() }, markMessageSeen: () => false, shouldDropMismatchedSlackEvent: () => false, resolveSlackSystemEventSessionKey: () => "agent:main:slack:channel:c1", @@ -123,7 +123,7 @@ describe("prepareSlackMessage sender prefix", () => { ackReactionScope: "off", mediaMaxBytes: 1000, removeAckAfterReply: false, - logger: { info: vi.fn() }, + logger: { info: vi.fn(), warn: vi.fn() }, markMessageSeen: () => false, shouldDropMismatchedSlackEvent: () => false, resolveSlackSystemEventSessionKey: () => "agent:main:slack:channel:c1",