{"record":{"id":"acb2783035cb3bd5","repo":"siyuan-note/siyuan","slug":"invalid-raw-websocket-declaration","errorCode":null,"errorMessage":"invalid raw WebSocket declaration","messagePattern":"invalid raw WebSocket declaration","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/apicontract/broadcast_protocol.go","lineNumber":39,"sourceCode":"\nfunc RawSSEOptions() ResponseOptions {\n\treturn ResponseOptions{Output: SSEOutput, SSE: &SSEDefinition{Raw: &RawSSEDefinition{EventNames: \"dynamic\", DataEncoding: \"raw\", ID: true, Retry: true}}}\n}\n\nfunc RawWebSocketOptions() ResponseOptions {\n\treturn ResponseOptions{Output: WebSocketOutput, WebSocket: &WebSocketDefinition{Raw: &RawWebSocketDefinition{Frames: []int{1, 2, 8, 9, 10}, UpgradeErrorStatuses: []int{400, 403, 405, 500}, EmptyClosedResponse: true}}}\n}\n\nfunc rawSSESchema(definition *SSEDefinition) (*SSESchema, error) {\n\tif len(definition.Events) != 0 || !reflect.DeepEqual(definition.Raw, RawSSEOptions().SSE.Raw) {\n\t\treturn nil, fmt.Errorf(\"invalid raw SSE event declaration\")\n\t}\n\treturn &SSESchema{Raw: definition.Raw}, nil\n}\n\nfunc rawWebSocketSchema(definition *WebSocketDefinition) (*WebSocketSchema, error) {\n\tif definition.Incoming != nil || definition.Outgoing != nil || definition.FailureStatus != 0 || !reflect.DeepEqual(definition.Raw, RawWebSocketOptions().WebSocket.Raw) {\n\t\treturn nil, fmt.Errorf(\"invalid raw WebSocket declaration\")\n\t}\n\treturn &WebSocketSchema{Incoming: &Schema{Type: \"string\", Format: \"binary\"}, Outgoing: &Schema{Type: \"string\", Format: \"binary\"}, Raw: definition.Raw}, nil\n}\n\nfunc validateRawWebSocketHTTP(schema *WebSocketSchema, status int, contentType string, payload []byte) (bool, error) {\n\tif schema.Raw == nil {\n\t\treturn false, nil\n\t}\n\tif status == 101 {\n\t\tif len(payload) != 0 {\n\t\t\treturn true, fmt.Errorf(\"WebSocket upgrade cannot contain a body\")\n\t\t}\n\t\treturn true, nil\n\t}\n\tif status == 200 && len(payload) == 0 && schema.Raw.EmptyClosedResponse {\n\t\treturn true, nil\n\t}\n\tmedia, _, _ := mime.ParseMediaType(contentType)","sourceCodeStart":21,"sourceCodeEnd":57,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/apicontract/broadcast_protocol.go#L21-L57","documentation":"rawWebSocketSchema validates that a WebSocket declaration in raw mode is untouched: Incoming and Outgoing typed schemas must be nil, FailureStatus must be zero, and Raw must deep-equal RawWebSocketOptions().WebSocket.Raw. Any typed-channel declaration or customized raw field makes the declaration invalid. Raw mode is a fixed wire passthrough, so customizations are not permitted.","triggerScenarios":"BuildBundle processes an endpoint whose WebSocket definition sets Incoming/Outgoing message schemas, sets a FailureStatus, or modifies Raw fields (Frames list, UpgradeErrorStatuses, EmptyClosedResponse) away from the canonical raw defaults.","commonSituations":"Declaring typed message contracts and raw frames together by mistake; copying a normal (evented) WebSocket definition and adding Raw: true; changing allowed frame types to suit a custom protocol.","solutions":["Remove Incoming and Outgoing schema declarations from the raw WebSocket definition","Reset FailureStatus to 0","Restore Raw to the exact value produced by RawWebSocketOptions().WebSocket.Raw","Declare a normal typed WebSocket endpoint instead if custom schemas are needed"],"exampleFix":"// before\nWebSocket: &WebSocketDefinition{Raw: customRaw, Incoming: &Schema{Type: \"object\"}}\n// after\nWebSocket: RawWebSocketOptions().WebSocket","handlingStrategy":"validation","validationCode":"if (def.incoming || def.outgoing || def.failureStatus) throw new Error(\"raw WebSocket cannot declare typed schemas\");\nif (!deepEqual(def.raw, RawWebSocketOptions().WebSocket.Raw)) throw new Error(\"raw WebSocket definition customized\");","typeGuard":"function isRawWSDef(d) { return !!d?.raw && !d.incoming && !d.outgoing && !d.failureStatus; }","tryCatchPattern":"try { schema = buildWS(def); } catch (e) { if (String(e).includes(\"invalid raw WebSocket declaration\")) { def = RawWebSocketOptions().WebSocket; schema = buildWS(def); } else throw e; }","preventionTips":["Raw mode and typed Incoming/Outgoing schemas are mutually exclusive","Reset FailureStatus for raw endpoints","Use RawWebSocketOptions() as the single source of truth","Re-run bundle builds in CI to catch declaration drift"],"tags":["api-contract","websocket","validation"],"backgroundTag":"invalid-config-value","analyzedSha":"9f775e8a12daef8255556097396f9b2739078892","analyzedAt":"2026-09-19T03:17:15.984Z","contentChangedAt":"2026-09-19T03:17:15.984Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}