DF0020: Non-JSON Value in JSON-Serializable RPC
Message
RPC function "
{name}" declaresjsonSerializable: truebut the value at "{path}" is a{type}.
Cause
The function is declared jsonSerializable: true, which means its args and return value are encoded with strict JSON.stringify (both on the wire and in build dumps). The strict serializer rejects any value that JSON cannot round-trip losslessly:
Map,Set,WeakMap,WeakSetDate(silently coerced to ISO string by JSON)BigInt- circular references
- non-plain class instances
undefinedleavesSymbolFunction
When the strict serializer encounters one of these, it throws synchronously at the offending call rather than producing a corrupt payload.
Example
defineRpcFunction({
name: 'my-plugin:graph',
jsonSerializable: true,
handler: () => ({
nodes: new Map([['a', 1]]), // ← throws DF0020 with type=Map, path="nodes"
}),
})Fix
Either drop jsonSerializable: true so the function uses structured-clone-es (round-trips Map, Set, etc.):
defineRpcFunction({
name: 'my-plugin:graph',
// jsonSerializable: false (default) — Map/Set survive the wire and the dump
handler: () => ({
nodes: new Map([['a', 1]]),
}),
})Or convert the payload to a JSON-safe shape (e.g. an array of entries, an ISO string, a plain object) before returning. Note: removing jsonSerializable: true also disables agent exposure; if you need MCP, you must use a JSON-safe shape.
Source
packages/devframe/src/rpc/serialization.ts— the strict JSON serializer throwsDF0020(with the offending path and runtime type) when ajsonSerializable: truepayload contains a non-JSON value.