{"record":{"id":"477fbf6e290499e5","repo":"siyuan-note/siyuan","slug":"decode-h-failed-s","errorCode":null,"errorMessage":"decode [h] failed: %s","messagePattern":"decode \\[h\\] failed: (.+?)","errorType":"validation","errorClass":null,"httpStatus":400,"severity":"error","filePath":"kernel/api/network.go","lineNumber":371,"sourceCode":"\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\n\t\tif jsonErr := json.Unmarshal(hBytes, &record); jsonErr != nil {\n\t\t\terr = fmt.Errorf(\"parse [h] failed: %s\", jsonErr.Error())\n\t\t\treturn\n\t\t}\n\n\t\tfor k, vs := range record {\n\t\t\tfor _, v := range vs {\n\t\t\t\th.Add(k, v)\n\t\t\t}\n\t\t}\n\t}\n\n\ttimeout = 30 * time.Second\n\ttParam := c.Query(\"t\")\n\tif tParam != \"\" {","sourceCodeStart":353,"sourceCodeEnd":389,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/api/network.go#L353-L389","documentation":"Returned by parseForwardProxyParams (network.go:371) when the optional `h` query parameter is present but cannot be base64-decoded with RawURLEncoding. `h` carries forwarded request headers as base64(RawURLEncoding) of a JSON map[string][]string; a malformed encoding is rejected with HTTP 400 before any header forwarding.","triggerScenarios":"Sending ?h=<standard-base64>, ?h=<raw-json>, or a corrupted encoding alongside a valid `u`. base64.RawURLEncoding.DecodeString at network.go:369 fails and the error is wrapped at line 371. httpProxy/wsProxy respond HTTP 400.","commonSituations":"Client used standard base64 (with '+','/','=' instead of URL-safe chars) for `h`. JSON.stringify of the header map was sent directly instead of base64-encoding it. Padding not stripped. Double URL-encoding mangled the alphabet.","solutions":["Encode the header JSON with base64 RawURLEncoding (URL-safe, no padding), the same way as `u`.","Build `h` from JSON.stringify({Header: [values]}) then URL-safe base64, stripping padding.","If you have no headers to forward, omit `h` entirely (it is optional)."],"exampleFix":"// before\nconst h = btoa(JSON.stringify({'X-Key':['v']})) // standard base64\n// after\nconst h = btoa(JSON.stringify({'X-Key':['v']})).replace(/\\+/g,'-').replace(/\\//g,'_').replace(/=+$/,'')","handlingStrategy":"validation","validationCode":"function encHeaders(h) {\n  const json = JSON.stringify(h); // h is map[string][]string shape\n  return btoa(json).replace(/\\+/g,'-').replace(/\\//g,'_').replace(/=+$/,'');\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Omit `h` entirely when no custom headers are needed.","Apply the same URL-safe-base64 helper used for `u`.","Validate base64 decodes round-trip in tests."],"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"}