{"record":{"id":"50cb2f634888ba19","repo":"siyuan-note/siyuan","slug":"path-and-content-required","errorCode":null,"errorMessage":"path and content required","messagePattern":"path and content required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/plugin/api_storage.go","lineNumber":235,"sourceCode":"\t\t})\n\t\tif runErr != nil {\n\t\t\tlogging.LogErrorf(\"[plugin:%s] siyuan.storage.get worker run: %v\", p.Name, runErr)\n\t\t\tif rejectErr := reject(rt.NewGoError(runErr)); rejectErr != nil {\n\t\t\t\tlogging.LogErrorf(\"[plugin:%s] siyuan.storage.get reject: %v\", p.Name, rejectErr)\n\t\t\t}\n\t\t}\n\n\t\treturn rt.ToValue(promise)\n\t})))\n\n\t// siyuan.storage.put(path, content) -> Promise<void>\n\tlo.Must0(storage.Set(\"put\", rt.ToValue(func(call goja.FunctionCall, rt *goja.Runtime) goja.Value {\n\t\tpromise, resolve, reject := rt.NewPromise()\n\n\t\tvar argErr error\n\t\tvar path, content string\n\t\tif len(call.Arguments) < 2 {\n\t\t\targErr = fmt.Errorf(\"path and content required\")\n\t\t} else {\n\t\t\tpath = call.Argument(0).String()\n\t\t\tcontent = call.Argument(1).String()\n\t\t}\n\n\t\trunErr := p.worker.Run(func(rt *goja.Runtime) (result any, err error) {\n\t\t\tif argErr != nil {\n\t\t\t\terr = argErr\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif util.ReadOnly {\n\t\t\t\terr = fmt.Errorf(\"The current kernel is in read-only mode, storage.put is not allowed\")\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tabs, resolveErr := resolvePath(path)\n\t\t\tif resolveErr != nil {\n\t\t\t\terr = resolveErr","sourceCodeStart":217,"sourceCodeEnd":253,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/plugin/api_storage.go#L217-L253","documentation":"Thrown by siyuan.storage.put() when fewer than two arguments are supplied. put writes content to a file under the plugin storage dir and requires both a path and a content string. The check is purely on argument count (len < 2), so omitting either the path or the content triggers it; note it does not additionally verify the types.","triggerScenarios":"Calling storage.put('x') with only a path and no content; put() with no arguments; put(undefined, undefined).","commonSituations":"Plugin builds the content lazily and forgets to pass it when empty; serialization step that produced undefined content was skipped; refactor changed put signatures and a call site was missed.","solutions":["Always pass both arguments: put(path, content).","Default content to an empty string when you intentionally want to truncate.","Validate that the content variable is defined before calling put."],"exampleFix":"// before\nawait siyuan.storage.put('state.json');\n// after\nawait siyuan.storage.put('state.json', JSON.stringify(state));","handlingStrategy":"validation","validationCode":"if (arguments.length < 2 || typeof path !== 'string') {\n  throw new TypeError('storage.put requires (path: string, content: string)');\n}","typeGuard":"const isPutArgs = (p, c) => typeof p === 'string' && typeof c === 'string';","tryCatchPattern":"try { await siyuan.storage.put(path, content); }\ncatch (e) { if (/path and content required/.test(String(e))) { /* missing arg */ } else throw e; }","preventionTips":["Default content to '' when intentionally truncating.","Serialize objects with JSON.stringify before passing as content."],"tags":["plugin-api","storage","argument-validation","javascript"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}