{"record":{"id":"23e6655fbe556796","repo":"siyuan-note/siyuan","slug":"method-name-required","errorCode":null,"errorMessage":"method name required","messagePattern":"method name required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/plugin/api_rpc.go","lineNumber":105,"sourceCode":"\t\t\t}\n\t\t})\n\t\tif runErr != nil {\n\t\t\tlogging.LogErrorf(\"[plugin:%s] siyuan.rpc.bind 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.rpc.bind reject: %v\", p.Name, rejectErr)\n\t\t\t}\n\t\t}\n\n\t\treturn rt.ToValue(promise)\n\t})))\n\n\tlo.Must0(rpc.Set(\"unbind\", 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 name string\n\t\tif len(call.Arguments) < 1 {\n\t\t\targErr = fmt.Errorf(\"method name required\")\n\t\t} else if nameArg := call.Argument(0); goja.IsString(nameArg) {\n\t\t\tname = nameArg.String()\n\t\t} else {\n\t\t\targErr = fmt.Errorf(\"first argument must be method name 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\terr = p.unbindRpcMethod(name)\n\t\t\treturn\n\t\t}, func(rt *goja.Runtime, result any, err error) {\n\t\t\tif lo.IsNil(err) {\n\t\t\t\tif resolveErr := resolve(result); resolveErr != nil {\n\t\t\t\t\tlogging.LogErrorf(\"[plugin:%s] siyuan.rpc.unbind resolve: %v\", p.Name, resolveErr)\n\t\t\t\t}","sourceCodeStart":87,"sourceCodeEnd":123,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/plugin/api_rpc.go#L87-L123","documentation":"Thrown by siyuan.rpc.unbind() when the plugin calls it with zero arguments. unbind deregisters an RPC method previously registered via siyuan.rpc.bind(name, fn). It requires the method name so the kernel knows which handler to remove; with no argument there is nothing to remove and the call is rejected before any worker work runs.","triggerScenarios":"Calling await siyuan.rpc.unbind() with no arguments, or calling unbind.apply(undefined, []) / Reflect.apply with an empty args array.","commonSituations":"Plugin code that conditionally unbinds a method but forgets to pass the name (e.g. unbind() instead of unbind('myMethod')), or refactor leftovers where a variable holding the name was removed but the call kept its empty parentheses.","solutions":["Pass the method name string that was used in the matching siyuan.rpc.bind(name, fn) call.","If you are iterating over a list of names, guard for empty arrays before calling unbind.","Keep bind/unbind name constants in a single shared module so they never drift out of sync."],"exampleFix":"// before\nawait siyuan.rpc.unbind();\n// after\nawait siyuan.rpc.unbind('myPlugin.myMethod');","handlingStrategy":"validation","validationCode":"function assertUnbindArgs(name) {\n  if (typeof name !== 'string' || name.length === 0) {\n    throw new TypeError('unbind requires a non-empty method name string');\n  }\n}\n// assertUnbindArgs(name); await siyuan.rpc.unbind(name);","typeGuard":"const isMethodName = (v) => typeof v === 'string' && v.length > 0;","tryCatchPattern":"try {\n  await siyuan.rpc.unbind(name);\n} catch (e) {\n  if (/method name required/.test(String(e))) { /* name missing: log and skip */ }\n  else { throw e; }\n}","preventionTips":["Keep bind/unbind name constants in one module and reference them at every call site.","Run a lint rule or unit test that asserts every unbind call passes a string literal or typed constant."],"tags":["plugin-api","rpc","argument-validation","javascript"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}