{"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":null,"severity":"error","filePath":"kernel/api/network.go","lineNumber":343,"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":325,"sourceCodeEnd":361,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/api/network.go#L325-L361","documentation":"The `u` query param of the forward-proxy endpoint must be base64 RawURLEncoding of the target URL. If base64 decoding fails (bad characters, wrong alphabet, padding, truncated data) the handler returns \"decode [u] failed: <reason>\".","triggerScenarios":"Sending `u` that is not valid RawURLEncoding base64: standard base64 with `+`/`/` or `=` padding, URL-encoding mangled characters, or a truncated value.","commonSituations":"Using base64.stdEncoding instead of RawURLEncoding in a custom client; double-encoding the value so `%2B` etc. arrive literally; copying a value and dropping trailing characters.","solutions":["Encode the target URL with base64.RawURLEncoding (Go) or a URL-safe base64 without padding (JS: btoa output with -/_ and no '=')","URL-encode the resulting string when placing it in the query string","Verify the decoded bytes are the exact URL string before sending (decode round-trip in your client)"],"exampleFix":"// before (standard base64 with padding)\nconst u = btoa(\"https://example.com\"); // aHR0cHM6Ly9leGFtcGxlLmNvbQ==\n// after (raw URL-safe base64, no padding)\nconst u = btoa(\"https://example.com\").replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\");","handlingStrategy":"validation","validationCode":"function toBase64Url(s) {\n  return btoa(s).replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\");\n}\nconst u = toBase64Url(targetUrl);","typeGuard":"function isValidBase64Url(s) {\n  return /^[A-Za-z0-9_-]+$/.test(s);\n}","tryCatchPattern":null,"preventionTips":["Use RawURLEncoding (Go) or a URL-safe no-padding encoder (JS) exclusively","Round-trip decode test the encoded value before sending"],"tags":["base64","encoding","proxy","query-param"],"backgroundTag":"invalid-argument-format","analyzedSha":"9f775e8a12daef8255556097396f9b2739078892","analyzedAt":"2026-09-19T03:17:15.984Z","contentChangedAt":"2026-09-19T03:17:15.984Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}