{"record":{"id":"17b779faa1b5999e","repo":"siyuan-note/siyuan","slug":"invalid-config-s-v","errorCode":null,"errorMessage":"invalid config.%s: %v","messagePattern":"invalid config\\.(.+?): (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/plugin/api_agent.go","lineNumber":305,"sourceCode":"\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":287,"sourceCodeEnd":309,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/plugin/api_agent.go#L287-L309","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nawait siyuan.agent.registerCapability('myTool', {\n  description: '...',\n  inputSchema: { type: 'object' },\n  effects: 'writes-blocks'  // string, not object\n}, handler);\n\n// after\nawait siyuan.agent.registerCapability('myTool', {\n  description: '...',\n  inputSchema: { type: 'object' },\n  effects: { writes: ['blocks'] }\n}, handler);","handlingStrategy":"validation","validationCode":"// Validate effects structure matches ToolEffects\nfunction validateEffects(effects) {\n  if (effects == null) return true;\n  if (typeof effects !== 'object' || Array.isArray(effects)) {\n    throw new Error('effects must be a plain object');\n  }\n  for (const key of Object.keys(effects)) {\n    if (Array.isArray(effects[key])) {\n      effects[key].forEach(v => {\n        if (typeof v !== 'string') throw new Error(`effects.${key} must be a string array`);\n      });\n    }\n  }\n}\nvalidateEffects(config.effects);\nvalidateEffects(config.actionEffects);","typeGuard":"function isPlainStringRecord(v) {\n  if (v == null) return true;\n  if (typeof v !== 'object' || Array.isArray(v)) return false;\n  return Object.values(v).every(val => Array.isArray(val) && val.every(x => typeof x === 'string'));\n}","tryCatchPattern":null,"preventionTips":["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"],"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"}