{"record":{"id":"93bc5949eba9f9b0","repo":"siyuan-note/siyuan","slug":"decode-u-failed-s","errorCode":null,"errorMessage":"decode [u] failed: %s","messagePattern":"decode \\[u\\] failed: (.+?)","errorType":"validation","errorClass":null,"httpStatus":400,"severity":"error","filePath":"kernel/api/network.go","lineNumber":356,"sourceCode":"\tclient.SetRedirectPolicy(req.MaxRedirectPolicy(3))\n\treturn client\n}\n\n// parseForwardProxyParams decodes the `u` and `h` query parameters.\n//\n// Query params:\n//   - `u`: RawURLEncoding base64 of the target URL string.\n//   - `h`: RawURLEncoding base64 of a JSON object map[string][]string.\n//   - `timeout`: The timeout for the request in nanoseconds.\nfunc parseForwardProxyParams(c *gin.Context) (parsedURL *url.URL, headers *http.Header, timeout time.Duration, err error) {\n\tuParam := c.Query(\"u\")\n\tif uParam == \"\" {\n\t\terr = fmt.Errorf(\"missing query param [u]\")\n\t\treturn\n\t}\n\tuBytes, decErr := base64.RawURLEncoding.DecodeString(uParam)\n\tif decErr != nil {\n\t\terr = fmt.Errorf(\"decode [u] failed: %s\", decErr.Error())\n\t\treturn\n\t}\n\tparsedURL, err = url.ParseRequestURI(string(uBytes))\n\tif err != nil {\n\t\terr = fmt.Errorf(\"parse [u] failed: %s\", err.Error())\n\t\treturn\n\t}\n\n\th := http.Header{}\n\theaders = &h\n\thParam := c.Query(\"h\")\n\tif hParam != \"\" {\n\t\thBytes, decErr := base64.RawURLEncoding.DecodeString(hParam)\n\t\tif decErr != nil {\n\t\t\terr = fmt.Errorf(\"decode [h] failed: %s\", decErr.Error())\n\t\t\treturn\n\t\t}\n\t\tvar record map[string][]string","sourceCodeStart":338,"sourceCodeEnd":374,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/api/network.go#L338-L374","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Encode the target URL with base64 RawURLEncoding (URL-safe alphabet, no padding): Go base64.RawURLEncoding.EncodeToString, or JS btoa then replace +->-, /->_, strip trailing '='.","Do NOT URL-encode the base64 string a second time; ensure it is placed raw in the query.","Verify the decoded output is a valid absolute http/https/ws/wss URL before sending."],"exampleFix":"// before\nconst u = btoa(targetUrl) // standard base64, fails RawURL decoding\n// after\nconst u = btoa(targetUrl).replace(/\\+/g,'-').replace(/\\//g,'_').replace(/=+$/,'')","handlingStrategy":"validation","validationCode":"function base64UrlSafe(s) {\n  return btoa(s).replace(/\\+/g,'-').replace(/\\//g,'_').replace(/=+$/,'');\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Use one shared URL-safe-base64 helper for all `u`/`h` params.","Never URL-encode the base64 string a second time.","Strip all padding '=' characters."],"tags":["network","proxy","validation","base64","kernel"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}