{"record":{"id":"0f5786cfe8559a36","repo":"siyuan-note/siyuan","slug":"missing-query-param-u","errorCode":null,"errorMessage":"missing query param [u]","messagePattern":"missing query param \\[u\\]","errorType":"validation","errorClass":null,"httpStatus":400,"severity":"error","filePath":"kernel/api/network.go","lineNumber":351,"sourceCode":"\tdialer := util.SSRFSafeDialer(timeout)\n\n\tclient := req.C()\n\tclient.SetTimeout(timeout)\n\tclient.SetDial(dialer.DialContext)\n\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)","sourceCodeStart":333,"sourceCodeEnd":369,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/api/network.go#L333-L369","documentation":"Returned by parseForwardProxyParams (network.go:351) when the `u` query parameter is absent or empty. The forward-proxy endpoints (httpProxy, wsProxy) require `u` to carry the base64(RawURLEncoding)-encoded target URL; without it there is no destination to proxy to, so the request is rejected with HTTP 400.","triggerScenarios":"Calling /api/network/forwardProxy (HTTP) or the WS proxy endpoint without ?u=... in the query string. parseForwardProxyParams at network.go:348 reads c.Query(\"u\") and returns this error at line 351 when it equals \"\". httpProxy/wsProxy then respond with HTTP 400 (network.go:421, 478).","commonSituations":"Frontend/proxy client forgot to append the `u` parameter. The base64-encoded URL was computed into a different variable and not added to the request URL. A misrouted request hitting the proxy endpoint without the expected query contract.","solutions":["Always include ?u=<base64rawurl(targetURL)> when calling the forward-proxy endpoints.","Build the query with a helper that asserts `u` is non-empty before firing the request.","Check that the target URL string is non-empty before base64-encoding it."],"exampleFix":"// before\nfetch(`/api/network/forwardProxy`)\n// after\nconst u = btoa(targetUrl).replace(/\\+/g,'-').replace(/\\//g,'_').replace(/=+$/,'')\nfetch(`/api/network/forwardProxy?u=${u}`)","handlingStrategy":"validation","validationCode":"// Build the proxy URL with a required non-empty target\nfunction proxyUrl(target) {\n  if (!target) throw new Error('target URL required');\n  const u = base64UrlSafe(target);\n  return `/api/network/forwardProxy?u=${u}`;\n}","typeGuard":null,"tryCatchPattern":"try { await fetch(proxyUrl(target)); }\ncatch (e) { if (/missing query param \\[u\\]/.test(e.msg)) { /* rebuild URL with u param */ } else throw e; }","preventionTips":["Centralize proxy-URL construction in one helper that asserts `u` is set.","Unit-test the helper against empty/undefined target values.","Treat a missing target as a programming error, not a retryable one."],"tags":["network","proxy","validation","kernel"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}