siyuan-note/siyuan · error
js value cannot be exported to a valid Go value
Error message
js value cannot be exported to a valid Go value
What it means
jsValueToBytes in the plugin sandbox converts a JavaScript value returned by a plugin handler into raw Go bytes. After checking all known JS types (e.g. ArrayBuffer, typed arrays, strings via buffer), none matched the runtime type of the value, so the bridge gives up rather than guessing a serialization. It signals a type contract violation: the plugin returned a JS value the Go side cannot represent as bytes.
Source
Thrown at kernel/plugin/sandbox.go:489
// jsValueToBytes attempts to convert a goja.Value to a byte slice, supporting string, Buffer, ArrayBuffer, etc.
func jsValueToBytes(rt *goja.Runtime, value goja.Value) (data []byte, err error) {
if goValue := value.Export(); goValue != nil {
switch d := goValue.(type) {
case string: // string
data = []byte(d)
case []byte: // Buffer
data = d
case goja.ArrayBuffer: // ArrayBuffer
data = d.Bytes()
case buffer.Buffer: // ?
data = buffer.Bytes(rt, value)
default:
err = fmt.Errorf("unsupported data type: %T", goValue)
}
return
}
err = fmt.Errorf("js value cannot be exported to a valid Go value")
return
}
// getRequestHandler retrieves the handler function and its containing object for a given scope and request type from the plugin's JS context.
func getRequestHandler(rt *goja.Runtime, scope AccessScope, requestType RequestType) (handler goja.Callable, handlerObj *goja.Object, err error) {
// Get handler object: siyuan.server[scope][requestType]
handlerObjValue, getObjErr := getJsContextValue(rt, []any{"siyuan", "server", string(scope), string(requestType)})
if getObjErr != nil {
err = getObjErr
return
}
handlerObj = handlerObjValue.ToObject(rt)
if handlerObj == nil {
err = fmt.Errorf("globalThis.siyuan.server[%s][%s] is not an object", scope, requestType)
return
}
View on GitHub (pinned to 8641553a1f)
Solutions
- Fix the plugin handler to return an ArrayBuffer, a typed array (Uint8Array etc.), or a string as its response body.
- If returning a Promise, await it inside the handler so the exported value is the resolved type.
- Serialize the object in JS first, e.g. `return JSON.stringify(obj)` or `new TextEncoder().encode(JSON.stringify(obj))`.
- Check kernel/plugin/sandbox.go jsValueToBytes for the list of supported types and add a conversion case if a new JS type must be supported (kernel-side change).
Example fix
// before (plugin JS)
handler: (req) => ({ ok: true, data: "..." });
// after
handler: (req) => JSON.stringify({ ok: true, data: "..." }); Defensive patterns
Strategy: type-guard
Validate before calling
function isExportableBody(v) {
return v instanceof ArrayBuffer || ArrayBuffer.isView(v) || typeof v === "string";
}
const body = await handler(req);
if (!isExportableBody(body)) throw new TypeError("handler must return ArrayBuffer, typed array, or string"); Type guard
function isExportableBody(v) {
return v instanceof ArrayBuffer || ArrayBuffer.isView(v) || typeof v === "string";
} Prevention
- Always return string, ArrayBuffer, or typed arrays from plugin handlers.
- Await Promises inside the handler before returning.
- Serialize objects with JSON.stringify before returning them.
When it happens
Trigger: A plugin request handler registered via globalThis.siyuan.server[scope][requestType].handler resolves/returns a value that is not an ArrayBuffer, typed array, or string (e.g. a plain object, undefined, null, a Promise resolving to an object, or a number), and the Go sandbox calls jsValueToBytes on it.
Common situations: Plugin authors returning JSON objects instead of binary/string bodies from a fetch-like handler; forgetting `await` so the handler returns a Promise; returning undefined from an early-exit code path; a plugin updated to a new response shape that the kernel version doesn't know.
Understand the failure class
Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.
Related errors
- globalThis.siyuan.server[%s][%s] is not an object
- injectAgent: %v
- injectPlugin: %v
- injectRpc: %v
- injectSecretsVars: %v
AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11).
Data as JSON: /api/errors/08bddeca568967b1.
Report an issue: GitHub.