siyuan-note/siyuan · error

decode [u] failed: %s

Error message

decode [u] failed: %s

What it means

Returned by parseForwardProxyParams (network.go:356) when the `u` query parameter is present but cannot be base64-decoded with RawURLEncoding. The decoder expects URL-safe base64 with no padding; standard base64, padded base64, hex, or raw text all fail decoding and the request is rejected with HTTP 400.

Source

Thrown at kernel/api/network.go:356

	client.SetRedirectPolicy(req.MaxRedirectPolicy(3))
	return client
}

// parseForwardProxyParams decodes the `u` and `h` query parameters.
//
// Query params:
//   - `u`: RawURLEncoding base64 of the target URL string.
//   - `h`: RawURLEncoding base64 of a JSON object map[string][]string.
//   - `timeout`: The timeout for the request in nanoseconds.
func parseForwardProxyParams(c *gin.Context) (parsedURL *url.URL, headers *http.Header, timeout time.Duration, err error) {
	uParam := c.Query("u")
	if uParam == "" {
		err = fmt.Errorf("missing query param [u]")
		return
	}
	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

View on GitHub (pinned to 251596fc0d)

Solutions

  1. Encode the target URL with base64 RawURLEncoding (URL-safe alphabet, no padding): Go base64.RawURLEncoding.EncodeToString, or JS btoa then replace +->-, /->_, strip trailing '='.
  2. Do NOT URL-encode the base64 string a second time; ensure it is placed raw in the query.
  3. Verify the decoded output is a valid absolute http/https/ws/wss URL before sending.

Example fix

// before
const u = btoa(targetUrl) // standard base64, fails RawURL decoding
// after
const u = btoa(targetUrl).replace(/\+/g,'-').replace(/\//g,'_').replace(/=+$/,'')
Defensive patterns

Strategy: validation

Validate before calling

function base64UrlSafe(s) {
  return btoa(s).replace(/\+/g,'-').replace(/\//g,'_').replace(/=+$/,'');
}

Prevention

When it happens

Trigger: Sending ?u=<standard-base64> (with '+'/'/'/'=' instead of '-'/['_']), ?u=<plaintext-url>, ?u=<hex>, or a corrupted/truncated encoding. base64.RawURLEncoding.DecodeString at network.go:354 returns an error and it is wrapped at line 356; httpProxy/wsProxy respond HTTP 400.

Common situations: Client used btoa() (standard base64) instead of URL-safe encoding. Padding '=' characters were not stripped. The URL was URL-encoded again, mangling the base64 alphabet. Copy-paste introduced whitespace or newlines.

Related errors


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