siyuan-note/siyuan · error
parse [h] failed: %s
Error message
parse [h] failed: %s
What it means
Returned by parseForwardProxyParams (network.go:376) when the `h` parameter decodes from base64 but is not valid JSON of the expected shape map[string][]string. After base64 decode, json.Unmarshal at network.go:375 fails — common causes are non-JSON text, a JSON object whose values are not arrays of strings (e.g. {"k":"v"} instead of {"k":["v"]}), or truncated JSON.
Source
Thrown at kernel/api/network.go:376
}
parsedURL, err = url.ParseRequestURI(string(uBytes))
if err != nil {
err = fmt.Errorf("parse [u] failed: %s", err.Error())
return
}
h := http.Header{}
headers = &h
hParam := c.Query("h")
if hParam != "" {
hBytes, decErr := base64.RawURLEncoding.DecodeString(hParam)
if decErr != nil {
err = fmt.Errorf("decode [h] failed: %s", decErr.Error())
return
}
var record map[string][]string
if jsonErr := json.Unmarshal(hBytes, &record); jsonErr != nil {
err = fmt.Errorf("parse [h] failed: %s", jsonErr.Error())
return
}
for k, vs := range record {
for _, v := range vs {
h.Add(k, v)
}
}
}
timeout = 30 * time.Second
tParam := c.Query("t")
if tParam != "" {
if t, parseErr := time.ParseDuration(tParam); parseErr != nil {
err = fmt.Errorf("parse [t] failed: %s", parseErr.Error())
return
} else {
timeout = tView on GitHub (pinned to 251596fc0d)
Solutions
- Structure header values as arrays of strings: {"X-Key":["v1","v2"], "Authorization":["Bearer x"]}.
- Validate the JSON parses and matches map[string][]string before base64-encoding.
- Strip BOM/leading whitespace; ensure the JSON is a single top-level object.
Example fix
// before
const h = enc(JSON.stringify({ 'X-Key': 'v' })) // value not an array
// after
const h = enc(JSON.stringify({ 'X-Key': ['v'] })) Defensive patterns
Strategy: type-guard
Validate before calling
// Ensure header map values are arrays of strings
function isHeaderMap(h) {
return typeof h === 'object' && h !== null && Object.values(h).every(v => Array.isArray(v) && v.every(x => typeof x === 'string'));
} Type guard
function isHeaderMap(h: unknown): h is Record<string, string[]> {
if (typeof h !== 'object' || h === null) return false;
return Object.values(h).every(v => Array.isArray(v) && v.every(x => typeof x === 'string'));
} Prevention
- Always shape header values as string arrays, even for single values.
- Validate the parsed JSON matches map[string][]string before encoding.
- Strip BOM and ensure the JSON is a single object.
When it happens
Trigger: Sending ?h=<base64-of-non-JSON> or ?h=<base64-of-{"k":"v"}> (string value instead of array). Trailing garbage after a valid JSON object. BOM or whitespace issues. The shape mismatch trips json.Unmarshal into map[string][]string at line 374.
Common situations: Client built the header map as {Header: 'value'} instead of {Header: ['value']}. JSON produced by a serializer that emits single strings for single-element headers. Encoding/charset mismatch inserted a BOM.
Related errors
- missing query param [u]
- decode [u] failed: %s
- parse [u] failed: %s
- decode [h] failed: %s
- parse [t] failed: %s
AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12).
Data as JSON: /api/errors/af9a3a77e8f3f67d.
Report an issue: GitHub.