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

  1. Pass a plain object literal as inputSchema (JSON Schema shape: {type:'object', properties:{...}, required:[...]})
  2. Remove functions, Symbols, circular references, and getters that throw from the schema object
  3. If the schema is computed at runtime, JSON.parse(JSON.stringify(schema)) it in JS before passing it in
  4. 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

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


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, err

View on GitHub (pinned to 9f775e8a12)