siyuan-note/siyuan · error
plugin WebSocket control frame exceeds 125 bytes
Error message
plugin WebSocket control frame exceeds 125 bytes
What it means
WebSocket control frames (opcodes 8 close, 9 ping, 10 pong) are limited by RFC 6455 to a payload of 125 bytes because they must fit in a single unfragmented frame. Bundle.ValidatePluginServiceFrame enforces this: when frameType >= 8 and len(payload) > 125 it returns "plugin WebSocket control frame exceeds 125 bytes". Oversized control frames break interoperability and are commonly rejected by other WebSocket implementations.
Solutions
- Truncate or drop the close-frame reason text so the control payload is at most 125 bytes (2 status bytes + reason)
- Send the data through text/binary frames (opcodes 1/2) instead of piggybacking on ping/pong
- Trim the payload to 125 bytes before emitting any control frame
- Add a size assertion in plugin tests for all close/ping/pong payloads
Example fix
// before
conn.WriteControl(8, append(statusBytes, longReason...), deadline)
// after
reason := longReason
if len(reason) > 123 { reason = reason[:123] }
conn.WriteControl(8, append(statusBytes, reason...), deadline) Defensive patterns
Strategy: validation
Validate before calling
func controlFrameFits(t int, payload []byte) bool { return t < 8 || len(payload) <= 125 } Try / catch
if err := bundle.ValidatePluginServiceFrame(method, path, frameType, payload); err != nil { if strings.Contains(err.Error(), "control frame exceeds 125 bytes") { truncateControlPayload(); return }; return err } Prevention
- Keep close-frame reasons within 123 bytes after the 2-byte status code
- Never transport application data in ping/pong frames
- Truncate control payloads before WriteControl
- Assert control-frame sizes in plugin tests
When it happens
Trigger: Calling Bundle.ValidatePluginServiceFrame with frameType 8, 9, or 10 and a payload longer than 125 bytes — e.g. a close frame carrying a long reason string, or a ping used to transport application data.
Common situations: A close status/reason string longer than 125 bytes is packed into a close frame; an application misuses ping/pong as a data channel; a plugin echoes back an upstream payload as a pong without truncating it; automated tests generate large control payloads.
Understand the failure class
Background: payload too large / request exceeds maximum size: why libraries cap bytes and how to fix oversize payloads — this error's family across 50 libraries.
Related errors
- undeclared plugin WebSocket frame
- WebSocket handshake contains a body
- failed to convert request value to object
- WebSocket control frame exceeds 125 bytes
- WebSocket is not open
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/0c11d2a72342b7aa.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/apicontract/plugin_service_protocol.go:271
for len(payload) > 0 {
_, _, size := protowire.ConsumeField(payload)
if size < 0 {
return protowire.ParseError(size)
}
payload = payload[size:]
}
}
return nil
}
func (b *Bundle) ValidatePluginServiceFrame(method, path string, frameType int, payload []byte) error {
for _, endpoint := range b.Endpoints {
if endpoint.Method == method && endpoint.Path == path && endpoint.PluginService != nil {
if frameType != 1 && frameType != 2 && frameType != 8 && frameType != 9 && frameType != 10 {
return fmt.Errorf("undeclared plugin WebSocket frame: %d", frameType)
}
if frameType >= 8 && len(payload) > 125 {
return fmt.Errorf("plugin WebSocket control frame exceeds 125 bytes")
}
return nil
}
}
return fmt.Errorf("unregistered plugin service: %s %s", method, path)
}
func (b *Bundle) ValidatePluginServiceEvent(method, path string, payload []byte) error {
for _, endpoint := range b.Endpoints {
if endpoint.Method == method && endpoint.Path == path && endpoint.PluginService != nil {
var event any
if err := json.Unmarshal(payload, &event); err != nil {
return err
}
return b.validate(endpoint.PluginService.SSEEvent, event, "$")
}
}
return fmt.Errorf("unregistered plugin service: %s %s", method, path)View on GitHub (pinned to 9f775e8a12)