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

  1. Inspect config.actionEffects keys and remove or fix the empty/whitespace key
  2. Use the exact action name matching the tool's action identifier
  3. Guard dynamic assignment: if (!actionName) throw/log before setting actionEffects[actionName]
  4. 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

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


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)