siyuan-note/siyuan · error
invalid config.%s: %v
Error message
invalid config.%s: %v
What it means
Thrown by unmarshalCapabilityJSON when the JSON-serialized effects or actionEffects value fails to unmarshal into the Go target type (tools.ToolEffects or map[string]tools.ToolEffects). The JSON is syntactically valid but structurally incompatible — wrong field types, unexpected nesting, or unknown required fields.
Source
Thrown at kernel/plugin/api_agent.go:305
actionEffects := map[string]tools.ToolEffects{}
if err := unmarshalCapabilityJSON(rt, value, &actionEffects, "actionEffects"); err != nil {
return nil, err
}
for action := range actionEffects {
if strings.TrimSpace(action) == "" {
return nil, fmt.Errorf("config.actionEffects contains an empty action")
}
}
return actionEffects, nil
}
func unmarshalCapabilityJSON(rt *goja.Runtime, value goja.Value, target any, field string) error {
jsonValue, err := value.ToObject(rt).MarshalJSON()
if err != nil {
return fmt.Errorf("failed to serialize config.%s: %v", field, err)
}
if err = json.Unmarshal(jsonValue, target); err != nil {
return fmt.Errorf("invalid config.%s: %v", field, err)
}
return nil
}
View on GitHub (pinned to 251596fc0d)
Solutions
- Match the effects object structure to the tools.ToolEffects Go struct definition
- Use plain objects with correct field types (string arrays for writes/reads, etc.)
- Check kernel/mcp/tools package for the current ToolEffects type definition
Example fix
// before
await siyuan.agent.registerCapability('myTool', {
description: '...',
inputSchema: { type: 'object' },
effects: 'writes-blocks' // string, not object
}, handler);
// after
await siyuan.agent.registerCapability('myTool', {
description: '...',
inputSchema: { type: 'object' },
effects: { writes: ['blocks'] }
}, handler); Defensive patterns
Strategy: validation
Validate before calling
// Validate effects structure matches ToolEffects
function validateEffects(effects) {
if (effects == null) return true;
if (typeof effects !== 'object' || Array.isArray(effects)) {
throw new Error('effects must be a plain object');
}
for (const key of Object.keys(effects)) {
if (Array.isArray(effects[key])) {
effects[key].forEach(v => {
if (typeof v !== 'string') throw new Error(`effects.${key} must be a string array`);
});
}
}
}
validateEffects(config.effects);
validateEffects(config.actionEffects); Type guard
function isPlainStringRecord(v) {
if (v == null) return true;
if (typeof v !== 'object' || Array.isArray(v)) return false;
return Object.values(v).every(val => Array.isArray(val) && val.every(x => typeof x === 'string'));
} Prevention
- Match the effects object structure to the tools.ToolEffects Go struct
- Use plain objects with string-array field values
- Reference kernel/mcp/tools for the current type definitions
When it happens
Trigger: Passing config.effects with incorrect structure — e.g., effects = 'read-only' (a string instead of an object), or effects = { writes: 'blocks' } (string instead of string array), where the Go struct expects specific field types.
Common situations: Plugin developer guesses the effects format instead of matching the tools.ToolEffects struct; a version change altered the ToolEffects struct shape; the effects object was built from untrusted external data.
Related errors
- config.actionEffects contains an empty action
- registerCapability requires 3 arguments: name, config, handl
- config.description must not be empty
- config.description is required and must be a string
- config.inputSchema is required
AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12).
Data as JSON: /api/errors/17b779faa1b5999e.
Report an issue: GitHub.