siyuan-note/siyuan · error
failed to export headers: %w
Error message
failed to export headers: %w
What it means
Thrown when the headers field of the fetch init object cannot be exported to map[string]string via goja's ExportTo. fetch requires headers as a flat string-to-string map; anything goja cannot coerce into that shape (arrays of pairs, nested objects, a Headers instance, non-string scalars) fails the export at api_client.go:74.
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 251596fc0d)
Solutions
- Pass headers as a plain object of string keys to string values: { 'Content-Type': 'application/json' }.
- If you have an array of pairs, convert first: Object.fromEntries(pairs).
- Stringify any non-string header value before putting it in the map.
Example fix
// before
await siyuan.client.fetch('/api/x', { headers: [['Content-Type','application/json']] });
// after
await siyuan.client.fetch('/api/x', { headers: { 'Content-Type': 'application/json' } }); Defensive patterns
Strategy: type-guard
Validate before calling
function normalizeHeaders(h) {
if (h == null) return {};
const out = {};
for (const [k, v] of Object.entries(h)) out[String(k)] = String(v);
return out;
}
await siyuan.client.fetch('/api/x', { headers: normalizeHeaders(init.headers) }); Type guard
const isStringMap = (v) => v != null && typeof v === 'object' && !Array.isArray(v) && Object.values(v).every(x => typeof x === 'string');
Prevention
- Never pass a Headers instance or entries array; use a plain object.
- Coerce all header values to strings before calling.
- Avoid nested objects in headers.
When it happens
Trigger: Passing headers as an array of pairs ([['Content-Type','application/json']]) like the browser Headers constructor, a nested object ({ a: { b: 'c' } }), a number-valued header, or an actual fetch Headers object.
Common situations: Developer ports browser fetch code that used new Headers(...) or an entries array, or sets a header to a number expecting automatic coercion (goja's ExportTo to map[string]string does not coerce).
Related errors
- path must start with /
- Failed to update agent session permission
- Agent capability name and description are required
- invalid frontend capability ID: %s
- frontend capability description is required: %s
AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12).
Data as JSON: /api/errors/abe0423cf7caea67.
Report an issue: GitHub.