siyuan-note/siyuan · error
unsupported data type: %T
Error message
unsupported data type: %T
What it means
jsValueToBytes converts a JS value to a Go byte slice, accepting string, []byte (Buffer), goja.ArrayBuffer, and buffer.Buffer. Any other exported Go type falls into the default branch and raises 'unsupported data type: %T' naming the actual type. It is the kernel's strictness when plugin APIs expect binary or text data.
Source
Thrown at kernel/plugin/sandbox.go:485
// isJsValueNotNull checks if a goja.Value is not nil, undefined or null.
func isJsValueNotNull(jsValue goja.Value) bool {
return isJsValueNotUndefined(jsValue) && !goja.IsNull(jsValue)
}
// 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 {View on GitHub (pinned to 8641553a1f)
Solutions
- Convert the value before calling the API: use JSON.stringify for objects, String(n) for numbers
- For binary data pass an ArrayBuffer or Uint8Array's .buffer; for text pass a string
- For DataView, pass dataView.buffer or new Uint8Array(dataView.buffer, dataView.byteOffset, dataView.byteLength)
Example fix
// before
pluginApi.writeData({ raw: bytes }); // object
// after
pluginApi.writeData(new Uint8Array(bytes).buffer); Defensive patterns
Strategy: validation
Validate before calling
function assertBytesLike(v) { if (typeof v !== 'string' && !(v instanceof ArrayBuffer) && !ArrayBuffer.isView(v)) { throw new TypeError('expected string, Buffer or ArrayBuffer, got ' + typeof v); } } Type guard
const isBytesLike = (v) => typeof v === 'string' || v instanceof ArrayBuffer || ArrayBuffer.isView(v);
Try / catch
try { api.writeData(payload); } catch (e) { if (String(e).includes('unsupported data type')) { api.writeData(String(payload)); } } Prevention
- Normalize payloads at the plugin boundary: strings for text, ArrayBuffer/Uint8Array for binary
- Convert objects with JSON.stringify and numbers with String() before passing to data APIs
- For DataView, pass .buffer with explicit byteOffset/byteLength
When it happens
Trigger: A plugin passes a non-binary JS value (plain object, number, boolean, array, function) to a kernel API whose data parameter is converted with jsValueToBytes — e.g. file/network APIs expecting string, Buffer, or ArrayBuffer.
Common situations: Passing a JSON object where a string/Buffer was expected; passing null/undefined-adjacent wrappers; passing a DataView (exports as plain object) instead of its .buffer ArrayBuffer; passing a number where a string was intended.
Related errors
- globalThis.siyuan.plugin.lifecycle is not an object
- path %v: expected object, got %T
- unsupported path type: %T
- globalThis.siyuan.event is not an object
- expected promise object, got %T
AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11).
Data as JSON: /api/errors/13aa52ee65cc020b.
Report an issue: GitHub.