siyuan-note/siyuan · error
failed to export headers
Error message
failed to export headers: %w
What it means
When init.headers is provided to siyuan.client.fetch, the bridge uses rt.ExportTo to convert the JS headers value into a Go http.Header. If the JS value cannot be exported into that Go type, this error rejects the fetch promise. Headers must be a plain object of string keys to (string or string[]) values.
Solutions
- Pass a plain object: { "Authorization": "token xxx", "Content-Type": "application/json" }
- Convert a Headers instance: Object.fromEntries(headers.entries())
- Convert a Map: Object.fromEntries(map)
- Ensure all values are strings (or arrays of strings) and header names are strings
Example fix
// before
const h = new Headers({ "Authorization": "token xx" });
await siyuan.client.fetch("/api/x", { headers: h })
// after
await siyuan.client.fetch("/api/x", { headers: { "Authorization": "token xx" } }) Defensive patterns
Strategy: type-guard
Validate before calling
function validateHeaders(h) {
if (h === undefined) return true;
if (typeof h !== "object" || h === null || Array.isArray(h)) return false;
return Object.entries(h).every(([k, v]) =>
typeof k === "string" && (typeof v === "string" || (Array.isArray(v) && v.every((x) => typeof x === "string"))));
} Type guard
const isPlainHeaderObject = (v) => v !== null && typeof v === "object" && !Array.isArray(v) && !(v instanceof Headers) && !(v instanceof Map);
Try / catch
try {
const res = await siyuan.client.fetch(path, { headers });
} catch (e) {
if (String(e).includes("failed to export headers")) {
headers = Object.fromEntries(
headers instanceof Headers ? headers.entries() : Object.entries(headers).map(([k, v]) => [k, String(v)])
);
}
} Prevention
- Pass headers as plain string-keyed objects with string values
- Convert Headers/Map instances with Object.fromEntries(...entries())
- Stringify numeric or structured header values before passing
When it happens
Trigger: Passing headers as a Headers instance, an array of [k,v] tuples, a Map, a function, or a string instead of a plain object; header values that are numbers/objects which Go cannot decode into http.Header.
Common situations: Reusing a browser Headers object built elsewhere; copying axios-style header arrays; passing numeric values like { "Content-Length": 123 }.
Related errors
- path must start with /
- path required
- panic during siyuan.client.fetch
- Access to encrypted notebook data is not supported via this…
- AI editor action must not be empty
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/abe0423cf7caea67.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/plugin/api_client.go:75
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 {
if b := initObj.Get("body"); isJsValueNotNull(b) {
if goja.IsString(b) {
bodyString = new(b.String())
} else {
body := b.Export()
if arrayBuffer, ok := body.(goja.ArrayBuffer); ok {
src := arrayBuffer.Bytes()
bodyBytes = new(src)
}
}
}
}
}
}View on GitHub (pinned to 9f775e8a12)