← Back to PRs

#20381: feat(gateway): make chat history byte limit configurable via `gateway.maxChatHistoryBytes`

by mgratch open 2026-02-18 21:01 View on GitHub →
gateway size: XS
## Summary - **Problem:** The WebSocket `maxPayload` limit (25 MB) is hardcoded in the gateway bundle. Users working with large payloads (browser screenshots as base64, file attachments over WS) cannot raise it without patching compiled JS. - **Why it matters:** A single high-res browser screenshot can exceed 25 MB as base64, disconnecting the client with "Max payload size exceeded" and no recovery path. - **What changed:** Added `gateway.maxPayloadBytes` config key in `openclaw.json`. The value is read once at startup and applied to both the primary and operator WebSocket servers. Falls back to 25 MB when unset. - **What did NOT change:** The default behavior is identical — no existing deployments are affected. `MAX_BUFFERED_BYTES` remains hardcoded (it's a send-side guard, not user-facing). The deprecated `MAX_PAYLOAD_BYTES` constant is preserved for any downstream imports. ## Change Type (select all) - [ ] Bug fix - [x] Feature - [ ] Refactor - [ ] Docs - [ ] Security hardening - [ ] Chore/infra ## Scope (select all touched areas) - [x] Gateway / orchestration - [ ] Skills / tool execution - [ ] Auth / tokens - [ ] Memory / storage - [ ] Integrations - [x] API / contracts - [ ] UI / DX - [ ] CI/CD / infra ## Linked Issue/PR - No linked issue — this is a standalone feature request. ## User-visible / Behavior Changes - New optional config key: `gateway.maxPayloadBytes` (number, bytes). When set in `openclaw.json`, overrides the default 25 MB WebSocket payload limit. - The configured value is also reported to clients in the handshake `policy.maxPayload` field, so clients can adjust their own limits accordingly. - Default behavior is unchanged when the key is absent. ## Security Impact (required) - New permissions/capabilities? `No` - Secrets/tokens handling changed? `No` - New/changed network calls? `No` - Command/tool execution surface changed? `No` - Data access scope changed? `No` ## Repro + Verification ### Environment - OS: macOS / Linux (Docker) - Runtime/container: OpenClaw v2026.2.18 - Model/provider: Any - Integration/channel: WebSocket (Control UI / WebChat) - Relevant config: `{ "gateway": { "maxPayloadBytes": 104857600 } }` (100 MB) ### Steps 1. Set `gateway.maxPayloadBytes` to a value above 25 MB in `openclaw.json`. 2. Restart the gateway. 3. Send a WebSocket message exceeding 25 MB (e.g. a base64 browser screenshot via computer use). ### Expected - Message is accepted and processed. Handshake `policy.maxPayload` reflects the configured value. ### Actual - Without this change: client is disconnected with "Max payload size exceeded". ## Evidence - [ ] Failing test/log before + passing after - [ ] Trace/log snippets - [ ] Screenshot/recording - [ ] Perf numbers (if relevant) Evidence will be provided after testing on the fork build. The change is minimal and follows the existing pattern used by `getMaxChatHistoryMessagesBytes` / `__setMaxChatHistoryMessagesBytesForTest`. ## Human Verification (required) - **Verified scenarios:** Code review of all `MAX_PAYLOAD_BYTES` references in the codebase to confirm both usage sites (WS server creation in `server-runtime-state.ts`, handshake policy in `message-handler.ts`) are updated. - **Edge cases checked:** Invalid config values (negative, zero, NaN, non-number) all fall back to the 25 MB default. Omitting the key preserves existing behavior. - **What I did not verify:** Load testing with extremely large payloads (>1 GB). The `ws` library itself may have its own practical limits. ## Compatibility / Migration - Backward compatible? `Yes` — the default is unchanged; no config migration needed. - Config/env changes? `Yes` — new optional key `gateway.maxPayloadBytes`. - Migration needed? `No` ## Failure Recovery (if this breaks) - **How to disable/revert:** Remove `gateway.maxPayloadBytes` from `openclaw.json` and restart. The gateway reverts to the hardcoded 25 MB default. - **Files/config to restore:** `openclaw.json` only. - **Known bad symptoms:** Setting an extremely large value could allow a malicious or buggy client to exhaust server memory with a single message. This is an operator-configured value on a self-hosted system, so the operator accepts the tradeoff. ## Risks and Mitigations - **Risk:** An operator sets an unreasonably large value and a single WS message exhausts memory. - **Mitigation:** This is an opt-in config on a self-hosted, typically single-user system. The default remains 25 MB. Documentation in the JSDoc notes the default and purpose. <!-- greptile_comment --> <h3>Greptile Summary</h3> This PR introduces a configurable `gateway.maxPayloadBytes` config key to override the hardcoded 25 MB WebSocket payload limit. The implementation adds `initMaxPayloadBytes()`/`getMaxPayloadBytes()` helpers in `server-constants.ts`, wires them into WS server creation and the handshake policy, and adds the TypeScript type. **Critical issue found:** - **Missing Zod schema entry**: `maxPayloadBytes` was added to the `GatewayConfig` TypeScript type but not to the Zod validation schema in `src/config/zod-schema.ts`. The gateway schema uses `.strict()`, which rejects unknown keys. Setting `gateway.maxPayloadBytes` in `openclaw.json` will fail config validation with `"Unrecognized key"`, making this feature non-functional. Add `maxPayloadBytes: z.number().int().positive().optional()` to the gateway object in the Zod schema. **Other observations:** - `MAX_BUFFERED_BYTES` (50 MB) remains hardcoded at 2× the old default. If an operator configures `maxPayloadBytes` above 50 MB, the send buffer limit could be smaller than a single message, potentially causing connection drops on large server→client responses. - The client-side `maxPayload` in `client.ts:114` stays hardcoded at 25 MB and doesn't read `policy.maxPayload` from the handshake. This is acceptable for the initial use case (large client→server uploads) but worth noting for completeness. - The commit message references `gateway.maxChatHistoryBytes` while the actual config key is `gateway.maxPayloadBytes`. <h3>Confidence Score: 2/5</h3> - This PR has a blocking bug — the new config key will be rejected by Zod validation at startup, making the feature non-functional. - The core logic (init/get helpers, WS server wiring, handshake policy) is correctly implemented, but the missing Zod schema entry means the feature cannot be used as intended. The gateway schema uses `.strict()`, so `gateway.maxPayloadBytes` in `openclaw.json` will fail config validation. This must be fixed before merging. - `src/config/zod-schema.ts` (not in the changeset but needs a corresponding entry for `maxPayloadBytes`), `src/gateway/server-constants.ts` (`MAX_BUFFERED_BYTES` may need to scale with the configured payload limit) <sub>Last reviewed commit: fbe9351</sub> <!-- greptile_other_comments_section --> <sub>(2/5) Greptile learns from your feedback when you react with thumbs up/down!</sub> <!-- /greptile_comment -->

Most Similar PRs