{"record":{"id":"8870a8f7fdf37128","repo":"siyuan-note/siyuan","slug":"invalid-jsonrpc-version-s","errorCode":null,"errorMessage":"invalid jsonrpc version: %s","messagePattern":"invalid jsonrpc version: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/plugin/rpc.go","lineNumber":116,"sourceCode":"\tdecoder := json.NewDecoder(bytes.NewReader(data))\n\t// decoder.DisallowUnknownFields() // Reject unknown fields violates the JSON-RPC spec\n\ttype JsonRpcRequestObject struct {\n\t\tJsonRpc util.Optional[string] `json:\"jsonrpc\"`\n\t\tMethod  util.Optional[string] `json:\"method\"`\n\t\tParams  util.Optional[any]    `json:\"params\"`\n\t\tID      util.Optional[any]    `json:\"id\"`\n\t}\n\trequest := JsonRpcRequestObject{}\n\tif err := decoder.Decode(&request); err != nil {\n\t\treturn err\n\t}\n\n\t// Validate jsonrpc field\n\tif !request.JsonRpc.Exists {\n\t\treturn fmt.Errorf(\"missing jsonrpc field\")\n\t}\n\tif request.JsonRpc.Value != JsonRpcVersion {\n\t\treturn fmt.Errorf(\"invalid jsonrpc version: %s\", request.JsonRpc.Value)\n\t}\n\n\t// Validate method field\n\tif !request.Method.HasValue() {\n\t\treturn fmt.Errorf(\"missing method field\")\n\t}\n\n\t// Validate id field\n\tif !request.ID.Exists {\n\t} else if request.ID.IsNull {\n\t} else if _, ok := request.ID.Value.(string); ok {\n\t} else if _, ok := request.ID.Value.(float64); ok {\n\t} else {\n\t\treturn fmt.Errorf(\"invalid id field: must be string, number, null or omitted\")\n\t}\n\n\tr.JsonRpc = request.JsonRpc.Value\n\tr.Method = request.Method.Value","sourceCodeStart":98,"sourceCodeEnd":134,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/plugin/rpc.go#L98-L134","documentation":"JsonRpcRequest.UnmarshalJSON requires the jsonrpc member to equal the supported JsonRpcVersion (\"2.0\"). When the field is present but holds any other value, unmarshalling fails with 'invalid jsonrpc version: %s' including the received value. This rejects JSON-RPC 1.x messages and typo'd version strings.","triggerScenarios":"Sending \"jsonrpc\":\"1.0\", \"jsonrpc\":\"2.0-rc\", \"jsonrpc\":\"JSON-RPC 2.0\", or a numeric jsonrpc: 2.0 (which decodes to \"2.0\"? no — as string field it errors on decode/type); any non-exact \"2.0\" string.","commonSituations":"Clients written against JSON-RPC 1.1/1.0; copy-pasted payloads with quoted or decorated version strings; serializers emitting numeric 2.0 instead of the string \"2.0\".","solutions":["Set the field to the exact string \"jsonrpc\": \"2.0\" (no spaces, no suffix)","Ensure the value is serialized as a string, not the number 2.0","Check the client library's protocol version setting and switch it to 2.0","Read the reported version value in the error message to see exactly what was received"],"exampleFix":"// before\n{\"jsonrpc\": 2.0, \"method\": \"ping\", \"id\": 1}\n// after\n{\"jsonrpc\": \"2.0\", \"method\": \"ping\", \"id\": 1}","handlingStrategy":"validation","validationCode":"if (body.jsonrpc !== \"2.0\") throw new Error(`jsonrpc must be exactly \"2.0\", got ${JSON.stringify(body.jsonrpc)}`);","typeGuard":"const isJsonRpc2 = (body) => body && typeof body.jsonrpc === \"string\" && body.jsonrpc === \"2.0\";","tryCatchPattern":"try { return await rpcSend(payload); } catch (e) { if (String(e).startsWith(\"invalid jsonrpc version\")) { payload.jsonrpc = \"2.0\"; return rpcSend(payload); } throw e; }","preventionTips":["Hard-code the exact string \"2.0\", never interpolate or localize it","Serialize jsonrpc as a string, not the number 2.0","Set client library protocol version to 2.0","Read the received value in the error message when diagnosing"],"tags":["json-rpc","validation","protocol-version"],"backgroundTag":"schema-validation-failed","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"}