siyuan-note/siyuan · error

decode [h] failed

Error message

decode [h] failed: %s

What it means

The optional `h` query param of the forward-proxy endpoint carries request headers as base64 RawURLEncoding of a JSON object map[string][]string. If base64 decoding of `h` fails, the handler returns "decode [h] failed: <reason>".

Solutions

  1. Encode the headers JSON with base64.RawURLEncoding (no padding, URL-safe alphabet)
  2. Only send `h` when you actually have headers; omit the param entirely otherwise (it is optional)
  3. Round-trip decode the value in your client to confirm correctness before the request

Example fix

// before
const h = btoa(JSON.stringify({"X-Token": ["abc"]})); // std base64 with padding
// after
const h = btoa(JSON.stringify({"X-Token": ["abc"]})).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
Defensive patterns

Strategy: validation

Validate before calling

const h = headers && Object.keys(headers).length ? toBase64Url(JSON.stringify(headers)) : null;

Type guard

function isValidBase64Url(s) {
  return /^[A-Za-z0-9_-]+$/.test(s);
}

Prevention

When it happens

Trigger: Supplying `h` that is not valid RawURLEncoding base64: standard base64 with padding or +// characters, mangling by URL encoding, or truncation.

Common situations: Custom scripts that JSON-encode headers but use stdEncoding base64; shell quoting stripping characters; hand-copying an encoded value from logs.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19). Data as JSON: /api/errors/477fbf6e290499e5. Report an issue: GitHub.

Appendix: source

Thrown at kernel/api/network.go:358

	}
	uBytes, decErr := base64.RawURLEncoding.DecodeString(uParam)
	if decErr != nil {
		err = fmt.Errorf("decode [u] failed: %s", decErr.Error())
		return
	}
	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 != "" {

View on GitHub (pinned to 9f775e8a12)