{"record":{"id":"7b98a7670c0e5d6b","repo":"siyuan-note/siyuan","slug":"failed-to-serialize-config-s-v","errorCode":null,"errorMessage":"failed to serialize config.%s: %v","messagePattern":"failed to serialize config\\.(.+?): (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/plugin/api_agent.go","lineNumber":302,"sourceCode":"}\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":284,"sourceCodeEnd":309,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/plugin/api_agent.go#L284-L309","documentation":"Thrown by unmarshalCapabilityJSON when MarshalJSON fails on the JS value representing config.effects or config.actionEffects. This is the same serialization failure as error 974 but for the effects/actionEffects fields — the value contains non-serializable content like functions or circular references.","triggerScenarios":"Passing config.effects or config.actionEffects that contains function values, circular references, or symbols. For example, effects = { writes: ['blocks'], compute: () => [] } where 'compute' cannot be JSON-serialized.","commonSituations":"Plugin includes methods or getters on the effects object; the effects object shares references creating cycles; a builder pattern left intermediate state on the object.","solutions":["Ensure the effects/actionEffects value is a plain JSON-serializable object","Strip non-serializable properties with JSON.parse(JSON.stringify(value)) before passing","Construct the effects object from plain data, not from class instances with methods"],"exampleFix":"// before\nconst effects = {\n  writes: ['blocks'],\n  validate: () => true  // function — not serializable\n};\nawait siyuan.agent.registerCapability('myTool', {\n  description: '...',\n  inputSchema: { type: 'object' },\n  effects\n}, handler);\n\n// after\nconst effects = {\n  writes: ['blocks']\n};","handlingStrategy":"validation","validationCode":"// Strip non-serializable properties from effects\nif (config.effects) {\n  config.effects = JSON.parse(JSON.stringify(config.effects));\n}\nif (config.actionEffects) {\n  config.actionEffects = JSON.parse(JSON.stringify(config.actionEffects));\n}","typeGuard":"function isJsonSerializable(obj) {\n  try {\n    JSON.stringify(obj);\n    return true;\n  } catch {\n    return false;\n  }\n}","tryCatchPattern":"try {\n  await siyuan.agent.registerCapability(name, config, handler);\n} catch (e) {\n  if (e.message.includes('serialize config.')) {\n    if (config.effects) config.effects = JSON.parse(JSON.stringify(config.effects));\n    if (config.actionEffects) config.actionEffects = JSON.parse(JSON.stringify(config.actionEffects));\n  }\n}","preventionTips":["Build effects objects from plain data, not class instances","Sanitize with JSON.parse(JSON.stringify()) to strip non-serializable members"],"tags":["plugin","agent","capability","effects","serialization","javascript"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}