siyuan-note/siyuan · error
failed to serialize inputSchema
Error message
failed to serialize inputSchema: %v
What it means
jsCapabilitySchemaToGoSchema converts a JavaScript tool capability's inputSchema object into a Go tools.ToolSchema. The first step serializes the JS object to JSON via goja's MarshalJSON; if that serialization fails, the function wraps and returns the error. This indicates the JS-side value could not be represented as JSON at all, typically because it is not a plain object or contains unserializable content.
Solutions
- Pass a plain object literal as inputSchema (JSON Schema shape: {type:'object', properties:{...}, required:[...]})
- Remove functions, Symbols, circular references, and getters that throw from the schema object
- If the schema is computed at runtime, JSON.parse(JSON.stringify(schema)) it in JS before passing it in
- Check the wrapped marshalErr message to identify the exact unserializable value
Example fix
// before
registerTool({ name: "myTool", inputSchema: makeSchema }) // function passed
// after
registerTool({ name: "myTool", inputSchema: { type: "object", properties: { q: { type: "string" } } } }) Defensive patterns
Strategy: validation
Validate before calling
function isPlainJsonSchema(v) {
return v !== null && typeof v === "object" && !Array.isArray(v) &&
JSON.parse(JSON.stringify(v)) !== undefined;
}
if (!isPlainJsonSchema(schema)) throw new Error("inputSchema must be a plain JSON object"); Type guard
const isPlainObject = (v) => typeof v === "object" && v !== null && !Array.isArray(v) && Object.getPrototypeOf(v) === Object.prototype;
Prevention
- Always define inputSchema as a literal JSON Schema object
- Never embed functions or class instances in schemas
- Deep-clone computed schemas with structuredClone before registering
When it happens
Trigger: A plugin registers a tool capability whose inputSchema argument to the capability API is not a plain JSON-serializable object (e.g. a function, a Symbol-containing object, a circular structure, or a Proxy with throwing traps passed as the schema).
Common situations: Plugin authors pass a getter-only object, a class instance with circular references, or accidentally pass undefined/a function where a schema object is expected; goja's MarshalJSON rejects the value.
Understand the failure class
Background: json.Marshal / "failed to marshal" errors in Go: why "unsupported type" happens and how to fix it — this error's family across 22 libraries.
Related errors
- failed to serialize config.
- invalid json schema
- globalThis.siyuan.plugin.lifecycle is not an object
- globalThis.siyuan.plugin.lifecycle not found
- globalThis.siyuan.plugin.lifecycle.
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/4bf5cb704d879f99.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/plugin/api_agent.go:263
logging.LogErrorf("[plugin:%s] siyuan.agent.unregisterCapability worker run: %v", p.Name, runErr)
if rejectErr := reject(rt.NewGoError(runErr)); rejectErr != nil {
logging.LogErrorf("[plugin:%s] siyuan.agent.unregisterCapability reject on run error: %v", p.Name, rejectErr)
}
}
return rt.ToValue(promise)
})))
lo.Must0(ObjectFreeze(rt, agentAPI))
lo.Must0(siyuan.Set("agent", agentAPI))
return
}
// jsCapabilitySchemaToGoSchema 将 JavaScript 能力 Schema 转换为 Go ToolSchema。
func jsCapabilitySchemaToGoSchema(rt *goja.Runtime, value goja.Value) (toolSchema *tools.ToolSchema, err error) {
schemaJson, marshalErr := value.ToObject(rt).MarshalJSON()
if marshalErr != nil {
err = fmt.Errorf("failed to serialize inputSchema: %v", marshalErr)
return
}
schema := &tools.ToolSchema{}
unmarshalErr := json.Unmarshal(schemaJson, schema)
if unmarshalErr != nil {
err = fmt.Errorf("invalid json schema: %v", unmarshalErr)
return
}
toolSchema = schema
return
}
func jsCapabilityEffectsToGoEffects(rt *goja.Runtime, value goja.Value) (*tools.ToolEffects, error) {
effects := &tools.ToolEffects{}
if err := unmarshalCapabilityJSON(rt, value, effects, "effects"); err != nil {
return nil, errView on GitHub (pinned to 9f775e8a12)