{"record":{"id":"7fae83b9c306f202","repo":"siyuan-note/siyuan","slug":"config-actioneffects-contains-an-empty-action","errorCode":null,"errorMessage":"config.actionEffects contains an empty action","messagePattern":"config\\.actionEffects contains an empty action","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/plugin/api_agent.go","lineNumber":293,"sourceCode":"\treturn\n}\n\nfunc jsCapabilityEffectsToGoEffects(rt *goja.Runtime, value goja.Value) (*tools.ToolEffects, error) {\n\teffects := &tools.ToolEffects{}\n\tif err := unmarshalCapabilityJSON(rt, value, effects, \"effects\"); err != nil {\n\t\treturn nil, err\n\t}\n\treturn effects, nil\n}\n\nfunc jsCapabilityActionEffectsToGoEffects(rt *goja.Runtime, value goja.Value) (map[string]tools.ToolEffects, error) {\n\tactionEffects := map[string]tools.ToolEffects{}\n\tif err := unmarshalCapabilityJSON(rt, value, &actionEffects, \"actionEffects\"); err != nil {\n\t\treturn nil, err\n\t}\n\tfor action := range actionEffects {\n\t\tif strings.TrimSpace(action) == \"\" {\n\t\t\treturn nil, fmt.Errorf(\"config.actionEffects contains an empty action\")\n\t\t}\n\t}\n\treturn actionEffects, nil\n}\n\nfunc unmarshalCapabilityJSON(rt *goja.Runtime, value goja.Value, target any, field string) error {\n\tjsonValue, err := value.ToObject(rt).MarshalJSON()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to serialize config.%s: %v\", field, err)\n\t}\n\tif err = json.Unmarshal(jsonValue, target); err != nil {\n\t\treturn fmt.Errorf(\"invalid config.%s: %v\", field, err)\n\t}\n\treturn nil\n}\n","sourceCodeStart":275,"sourceCodeEnd":309,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/plugin/api_agent.go#L275-L309","documentation":"Thrown by jsCapabilityActionEffectsToGoEffects after successfully unmarshaling the actionEffects map — one or more keys (action names) in the map are empty or whitespace-only after strings.TrimSpace. Action names are used as identifiers in the declared effects map, so empty keys would be ambiguous.","triggerScenarios":"Passing config.actionEffects = { '': {...} } or config.actionEffects = { '  ': {...} } — a map with at least one empty/whitespace key. This can happen when action names are dynamically generated from data and one source value is empty.","commonSituations":"Plugin generates action names from user input or document data that may be empty; a default empty-string key was accidentally included; the actionEffects map was built from a list that contained an empty element.","solutions":["Remove empty or whitespace-only keys from the actionEffects map before passing it","Validate action names are non-empty when constructing the actionEffects object"],"exampleFix":"// before\nconst actionEffects = {\n  '': { writes: ['blocks'] },\n  'create': { writes: ['blocks'] }\n};\nawait siyuan.agent.registerCapability('myTool', {\n  description: '...',\n  inputSchema: { type: 'object' },\n  actionEffects\n}, handler);\n\n// after\nconst actionEffects = {\n  'create': { writes: ['blocks'] }\n};","handlingStrategy":"validation","validationCode":"// Remove empty action keys before passing\nif (config.actionEffects) {\n  for (const key of Object.keys(config.actionEffects)) {\n    if (key.trim() === '') {\n      delete config.actionEffects[key];\n    }\n  }\n}","typeGuard":"function hasNoEmptyActionKeys(actionEffects) {\n  if (!actionEffects) return true;\n  return Object.keys(actionEffects).every(k => k.trim().length > 0);\n}","tryCatchPattern":null,"preventionTips":["Filter out empty action names when constructing the actionEffects map","Validate action name sources (user input, document data) for emptiness"],"tags":["plugin","agent","capability","effects","validation","javascript"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}