siyuan-note/siyuan · error
config.actionEffects contains an empty action
Error message
config.actionEffects contains an empty action
What it means
jsCapabilityActionEffectsToGoEffects converts the config.actionEffects map (action name -> effects) from JS. It rejects the configuration when any key (action name) is empty or whitespace-only. Effect keys are used to match tool invocations, so an empty key would be meaningless and likely a configuration mistake.
Solutions
- Inspect config.actionEffects keys and remove or fix the empty/whitespace key
- Use the exact action name matching the tool's action identifier
- Guard dynamic assignment: if (!actionName) throw/log before setting actionEffects[actionName]
- Use Object.keys(config.actionEffects).some(k => !k.trim()) to self-check before registering
Example fix
// before
const actionEffects = {}; actionEffects[getName()] = { readOnlyHint: true }; // getName() returned ""
// after
const name = getName();
if (!name || !name.trim()) throw new Error("action name must not be empty");
const actionEffects = { [name]: { readOnlyHint: true } }; Defensive patterns
Strategy: validation
Validate before calling
function hasEmptyActionKey(actionEffects) {
return Object.keys(actionEffects || {}).some((k) => !k || !k.trim());
}
if (hasEmptyActionKey(config.actionEffects)) throw new Error("actionEffects has an empty action key"); Prevention
- Check dynamic action names for emptiness before assigning keys
- Avoid building keys from possibly-empty template literals
- Lint with Object.keys() assertions in plugin tests
When it happens
Trigger: A plugin defines config.actionEffects with an empty-string key, e.g. actionEffects: { "": {readOnlyHint:true} }, or a key of only whitespace like " ", produced by template literals or dynamic key construction from empty variables.
Common situations: Building keys programmatically (actionEffects[actionName] = ...) where actionName is empty; copy-paste leaving a blank key; destructuring mistakes creating a "" key.
Understand the failure class
Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.
Related errors
- Argon2id Iterations too high (maximum 10)
- Argon2id Memory too high (maximum 256 MB)
- Argon2id Parallelism must be between 1 and 16
- builtin color must not be null
- builtin style must not be null
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/7fae83b9c306f202.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/plugin/api_agent.go:293
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
}
return effects, nil
}
func jsCapabilityActionEffectsToGoEffects(rt *goja.Runtime, value goja.Value) (map[string]tools.ToolEffects, error) {
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)