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 = t

View on GitHub (pinned to 251596fc0d)

Solutions

  1. Structure header values as arrays of strings: {"X-Key":["v1","v2"], "Authorization":["Bearer x"]}.
  2. Validate the JSON parses and matches map[string][]string before base64-encoding.
  3. 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

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


AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12). Data as JSON: /api/errors/af9a3a77e8f3f67d. Report an issue: GitHub.