{"record":{"id":"b564db91b827edaf","repo":"siyuan-note/siyuan","slug":"invalid-id-field-must-be-string-number-null-or-omitted","errorCode":null,"errorMessage":"invalid id field: must be string, number, null or omitted","messagePattern":"invalid id field: must be string, number, null or omitted","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/apicontract/plugin_rpc.go","lineNumber":20,"sourceCode":"\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"strings\"\n)\n\ntype PluginRPCID struct{ value JSONValue }\n\nfunc (id PluginRPCID) MarshalJSON() ([]byte, error) { return json.Marshal(id.value) }\nfunc (id *PluginRPCID) UnmarshalJSON(data []byte) error {\n\tdata = bytes.TrimSpace(data)\n\tif !json.Valid(data) || len(data) == 0 {\n\t\treturn fmt.Errorf(\"invalid RPC ID\")\n\t}\n\tif data[0] != '\"' && data[0] != '-' && (data[0] < '0' || data[0] > '9') && string(data) != \"null\" {\n\t\treturn fmt.Errorf(\"invalid id field: must be string, number, null or omitted\")\n\t}\n\treturn json.Unmarshal(data, &id.value)\n}\n\ntype PluginRPCParams struct{ value JSONValue }\n\ntype PluginRPCRequestFields struct {\n\tJSONRPC string           `json:\"jsonrpc\" api:\"const=\\\"2.0\\\"\"`\n\tMethod  string           `json:\"method\"`\n\tParams  *PluginRPCParams `json:\"params\" api:\"optional\"`\n\tID      *PluginRPCID     `json:\"id\" api:\"optional\"`\n}\n\ntype PluginRPCSuccess struct {\n\tJSONRPC string      `json:\"jsonrpc\" api:\"const=\\\"2.0\\\"\"`\n\tResult  JSONValue   `json:\"result\"`\n\tID      PluginRPCID `json:\"id\"`\n}","sourceCodeStart":2,"sourceCodeEnd":38,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/apicontract/plugin_rpc.go#L2-L38","documentation":"After confirming the id is valid JSON, PluginRPCID.UnmarshalJSON enforces the JSON-RPC id rules: it must be a string (starts with '\"'), a number (starts with '-' or 0-9), or the literal null. Any other JSON value — typically an object or array — is rejected with \"invalid id field: must be string, number, null or omitted\".","triggerScenarios":"Unmarshalling a plugin RPC message whose id is a JSON object or array (data[0] is '{' or '['), e.g. {\"id\": {\"n\": 1}} or {\"id\": [\"a\"]}.","commonSituations":"A client library that types id as a generic object serializes a structured id; correlation IDs wrapped in objects for extra metadata; mismatched RPC dialects between plugin and kernel.","solutions":["Change the id to a plain string or number (or null) as required by the JSON-RPC id specification.","Move any extra correlation metadata into params instead of the id field.","Fix the client's id type declaration so it serializes as a scalar."],"exampleFix":"// before\n{\"id\": {\"requestId\": 42}}\n// after\n{\"id\": 42}","handlingStrategy":"validation","validationCode":"function checkRpcIdShape(id) {\n  if (!(typeof id === \"string\" || (typeof id === \"number\" && Number.isFinite(id)) || id === null)) {\n    throw new Error(\"id must be a JSON-RPC scalar: string, number, or null\");\n  }\n}","typeGuard":"const isScalarRpcId = (id) => id === null || typeof id === \"string\" || (typeof id === \"number\" && !Number.isNaN(id));","tryCatchPattern":"try { await rpcCall(method, params, id); } catch (e) { if (String(e).includes(\"invalid id field\")) { throw new Error(\"wrap id metadata into params; id must stay a scalar\"); } throw e; }","preventionTips":["Type the RPC id as string | number | null in the client.","Never send objects or arrays as the JSON-RPC id.","Put correlation metadata in params, not the id."],"tags":["json","rpc","validation"],"backgroundTag":"invalid-argument-format","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"}