siyuan-note/siyuan · error

failed to serialize inputSchema: %v

Error message

failed to serialize inputSchema: %v

What it means

Thrown by jsCapabilitySchemaToGoSchema when value.ToObject(rt).MarshalJSON() fails. This converts the JS inputSchema (or outputSchema) value to JSON for unmarshaling into the Go tools.ToolSchema struct. MarshalJSON fails when the value contains non-serializable content such as circular references, functions, or symbols.

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 251596fc0d)

Solutions

  1. Ensure the inputSchema is a plain JSON-serializable object — no functions, symbols, or circular references
  2. Use JSON.parse(JSON.stringify(schema)) before passing to strip non-serializable properties
  3. If using a schema builder library, call its .toJSON() or equivalent to get a plain object

Example fix

// before
await siyuan.agent.registerCapability('myTool', {
  description: '...',
  inputSchema: {
    type: 'object',
    validate: (val) => val > 0  // function — not serializable
  }
}, handler);

// after
await siyuan.agent.registerCapability('myTool', {
  description: '...',
  inputSchema: {
    type: 'object',
    properties: {
      val: { type: 'number', minimum: 1 }
    }
  }
}, handler);
Defensive patterns

Strategy: validation

Validate before calling

// Strip non-serializable properties before passing
const cleanSchema = JSON.parse(JSON.stringify(inputSchema));
if (!cleanSchema) {
  throw new Error('inputSchema must be JSON-serializable');
}
config.inputSchema = cleanSchema;
await siyuan.agent.registerCapability(name, config, handler);

Type guard

function isJsonSerializable(obj) {
  try {
    JSON.stringify(obj);
    return true;
  } catch {
    return false;
  }
}

Try / catch

try {
  await siyuan.agent.registerCapability(name, config, handler);
} catch (e) {
  if (e.message.includes('serialize inputSchema')) {
    // schema has non-serializable content — strip and retry
    config.inputSchema = JSON.parse(JSON.stringify(config.inputSchema));
  }
}

Prevention

When it happens

Trigger: Passing an inputSchema that contains function values, circular references, or goja-specific non-serializable objects. For example, inputSchema = { type: 'object', validate: () => true } where 'validate' is a function that cannot be JSON-serialized.

Common situations: Plugin includes validator functions or computed getters in the schema object; the schema references itself; a prototype pollution or incorrect object construction introduces non-serializable members.

Related errors


AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12). Data as JSON: /api/errors/4bf5cb704d879f99. Report an issue: GitHub.