siyuan-note/siyuan · error
invalid config.
Error message
invalid config.%s: %v
What it means
unmarshalCapabilityJSON decodes the serialized JSON of a named config field into its Go target struct. If json.Unmarshal fails (the JSON does not fit the target's types), this error is returned with the field name. The value serializes fine but has the wrong shape or types for the capability config.
Solutions
- Match the documented ToolEffects shape: e.g. { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true }
- Use boolean values for hint fields, not strings or numbers
- Read the wrapped invalid config.<field> message to see the exact decode failure
- Compare against a working plugin sample's config
Example fix
// before
config.effects = { run: "readonly" }
// after
config.effects = { run: { readOnlyHint: true, destructiveHint: false } } Defensive patterns
Strategy: validation
Validate before calling
function validateEffects(effects) {
return Object.entries(effects || {}).every(([k, v]) =>
k && typeof v === "object" && v !== null &&
Object.values(v).every((x) => typeof x === "boolean"));
} Type guard
const isToolEffects = (v) => v !== null && typeof v === "object" && !Array.isArray(v) && Object.values(v).every((x) => typeof x === "boolean");
Try / catch
try {
registerCapability(config);
} catch (e) {
if (String(e).includes("invalid config.")) {
console.error("Capability config shape/type mismatch", e);
}
} Prevention
- Copy the ToolEffects shape from the official sample plugin
- Use booleans for all hint fields
- Round-trip check: JSON.parse(JSON.stringify(config)) should keep the same shape
When it happens
Trigger: config.effects or config.actionEffects has wrong-typed members, e.g. effects: {run: "readonly"} instead of an effects object, or actionEffects values missing expected fields / with wrong types like numeric hints.
Common situations: Authors guess the config shape instead of following the ToolEffects struct; using string flags where booleans are expected; nesting the map one level too deep or shallow.
Related errors
- failed to serialize config.
- map[string]apicontract.JSONValue
- struct field Sync .
- block ID must be text
- block operation result must be text, block IDs or null
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/17b779faa1b5999e.
Report an issue: GitHub.
Appendix: 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 9f775e8a12)