siyuan-note/siyuan · error
invalid RPC ID
Error message
invalid RPC ID
What it means
PluginRPCID wraps the JSON-RPC-style "id" field of plugin RPC requests. Its UnmarshalJSON first requires the raw value to be valid, non-empty JSON; otherwise it returns "invalid RPC ID". This catches syntactically broken or empty id payloads before the per-type member check (string/number/null).
Solutions
- Send the id as a proper JSON value: a quoted string, a number, or null (or omit the field where allowed).
- Use JSON.stringify / json.Marshal on the whole message instead of manual string building.
- Validate the message body with a JSON parser before posting it to the RPC endpoint.
Example fix
// before
{"jsonrpc":"2.0","id":undefined,"method":"x"} // -> {"id":} invalid
// after
{"jsonrpc":"2.0","id":"req-1","method":"x"} Defensive patterns
Strategy: try-catch
Validate before calling
function checkRpcId(id) { if (!(typeof id === "string" || typeof id === "number" || id === null)) throw new Error("rpc id must be string, number or null"); } Type guard
const isValidRpcId = (id) => typeof id === "string" || (typeof id === "number" && Number.isFinite(id)) || id === null;
Try / catch
try { const parsed = JSON.parse(msg); send(parsed); } catch (e) { if (String(e).includes("invalid RPC ID")) { regenerateAndResendWithScalarId(); } else { throw e; } } Prevention
- Serialize the whole RPC message with JSON.stringify, never string concatenation.
- Use a counter or uuid string as the id.
- Reject non-scalar ids at message construction time.
When it happens
Trigger: Unmarshalling a plugin RPC message whose id field is invalid JSON (e.g. raw bytes like `undefined`, a bare token, or truncated data), or an empty/whitespace-only value.
Common situations: A plugin builds the JSON-RPC message by string concatenation and emits a non-JSON id (undefined in JS serializes oddly or is dropped); truncated transport frames; hand-written test fixtures with invalid JSON.
Understand the failure class
Background: "failed to unmarshal" / json.Unmarshal errors: why parsing a response into a Go struct fails and how to fix it — this error's family across 23 libraries.
Related errors
- failed to serialize config.
- failed to serialize inputSchema
- first argument must be method name string
- invalid config.
- invalid id field: must be string, number, null or omitted
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/3dc5f11c246da878.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/apicontract/plugin_rpc.go:17
package apicontract
import (
"bytes"
"encoding/json"
"fmt"
"io"
"strings"
)
type PluginRPCID struct{ value JSONValue }
func (id PluginRPCID) MarshalJSON() ([]byte, error) { return json.Marshal(id.value) }
func (id *PluginRPCID) UnmarshalJSON(data []byte) error {
data = bytes.TrimSpace(data)
if !json.Valid(data) || len(data) == 0 {
return fmt.Errorf("invalid RPC ID")
}
if data[0] != '"' && data[0] != '-' && (data[0] < '0' || data[0] > '9') && string(data) != "null" {
return fmt.Errorf("invalid id field: must be string, number, null or omitted")
}
return json.Unmarshal(data, &id.value)
}
type PluginRPCParams struct{ value JSONValue }
type PluginRPCRequestFields struct {
JSONRPC string `json:"jsonrpc" api:"const=\"2.0\""`
Method string `json:"method"`
Params *PluginRPCParams `json:"params" api:"optional"`
ID *PluginRPCID `json:"id" api:"optional"`
}
type PluginRPCSuccess struct {
JSONRPC string `json:"jsonrpc" api:"const=\"2.0\""`View on GitHub (pinned to 9f775e8a12)