siyuan-note/siyuan · error
path required
Error message
path required
What it means
Thrown by the siyuan.client.fetch JS API exposed to plugins when the first argument is not a string. The fetch function routes HTTP requests to the kernel's own loopback server (http://127.0.0.1:{port}{path}), and the path must be a string starting with '/'. If goja.IsString returns false on call.Argument(0), the path is considered absent.
Source
Thrown at kernel/plugin/api_client.go:61
}
}()
client := rt.NewObject()
lo.Must0(client.Set("fetch", rt.ToValue(func(call goja.FunctionCall, rt *goja.Runtime) goja.Value {
promise, resolve, reject := rt.NewPromise()
var argErr error
var path string
method := "GET"
headers := map[string]string{}
var bodyString *string
var bodyBytes *[]byte
if goja.IsString(call.Argument(0)) {
path = call.Argument(0).String()
} else {
argErr = fmt.Errorf("path required")
}
if argErr == nil && !strings.HasPrefix(path, "/") {
argErr = fmt.Errorf("path must start with /")
}
if argErr == nil {
if init := call.Argument(1); isJsValueNotNull(init) {
if initObj := init.ToObject(rt); initObj != nil {
if m := initObj.Get("method"); goja.IsString(m) {
method = m.String()
}
if h := initObj.Get("headers"); isJsValueNotNull(h) {
if exportErr := rt.ExportTo(h, &headers); exportErr != nil {
argErr = fmt.Errorf("failed to export headers: %w", exportErr)
}
}
if argErr == nil {View on GitHub (pinned to 251596fc0d)
Solutions
- Pass the API path as a plain string starting with '/', e.g., '/api/notebook/lsNotebooks'
- If using a URL object, convert to string and extract just the path portion
- Ensure the path argument is the first positional parameter
Example fix
// before
await siyuan.client.fetch({ url: '/api/notebook/lsNotebooks' });
// after
const resp = await siyuan.client.fetch('/api/notebook/lsNotebooks', {
method: 'POST',
body: JSON.stringify({})
}); Defensive patterns
Strategy: type-guard
Validate before calling
if (typeof path !== 'string' || !path.startsWith('/')) {
throw new Error('First argument to siyuan.client.fetch must be a path string starting with /');
}
const resp = await siyuan.client.fetch(path, init); Type guard
function isFetchPathArg(v) {
return typeof v === 'string' && v.startsWith('/');
} Try / catch
try {
const resp = await siyuan.client.fetch(path, { method: 'POST', body: JSON.stringify(payload) });
} catch (e) {
console.error('siyuan.client.fetch failed:', e.message);
} Prevention
- Always pass the API path as the first string argument starting with '/'
- This fetch is NOT the browser fetch API — path comes first, then the init object
- Do not pass a URL object or options object as the first argument
When it happens
Trigger: Calling siyuan.client.fetch() with no arguments, or with a non-string first argument such as a number, object, or URL instance. Example: siyuan.client.fetch(42) or siyuan.client.fetch({ url: '/api/...' }).
Common situations: Plugin developer passes an options object first (like browser fetch's init); uses a URL object instead of a string path; the path variable was never assigned; a refactor changed the call signature.
Related errors
- first argument must be a tool name string
- third argument must be a handler function
- registerCapability requires 3 arguments: name, config, handl
- config.description must not be empty
- config.description is required and must be a string
AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12).
Data as JSON: /api/errors/33a942f5eab9a226.
Report an issue: GitHub.