{"record":{"id":"c3d7c801273db51a","repo":"siyuan-note/siyuan","slug":"invalid-request","errorCode":null,"errorMessage":"invalid request: ","messagePattern":"invalid request: ","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/util/httprequest.go","lineNumber":327,"sourceCode":"\t\treturn 0, \"\", \"\", errors.New(\"URL has no host\")\n\t}\n\n\tif serr := CheckHostSSRF(u.Hostname()); serr != nil {\n\t\treturn 0, \"\", \"\", serr\n\t}\n\n\tmethod = strings.ToUpper(strings.TrimSpace(method))\n\tif method == \"\" {\n\t\tmethod = \"GET\"\n\t}\n\n\tvar reqBody io.Reader\n\tif body != \"\" && method != \"GET\" && method != \"HEAD\" {\n\t\treqBody = strings.NewReader(body)\n\t}\n\treq, err := http.NewRequest(method, rawURL, reqBody)\n\tif err != nil {\n\t\treturn 0, \"\", \"\", errors.New(\"invalid request: \" + err.Error())\n\t}\n\tfor k, v := range headers {\n\t\treq.Header.Set(k, v)\n\t}\n\n\tresp, err := ssrfSafeClient.Do(req)\n\tif err != nil {\n\t\treturn 0, \"\", \"\", errors.New(\"request failed: \" + err.Error())\n\t}\n\tif resp == nil {\n\t\treturn 0, \"\", \"\", errors.New(\"nil response\")\n\t}\n\tdefer resp.Body.Close()\n\n\tstatusCode = resp.StatusCode\n\tcontentType = resp.Header.Get(\"Content-Type\")\n\n\tmaxReadBytes := int64(maxHTTPRequestBytes)","sourceCodeStart":309,"sourceCodeEnd":345,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/util/httprequest.go#L309-L345","documentation":"http.NewRequest builds the outbound request; if it errors (unparseable URL at the request level, invalid method token, nil body misuse), the error is wrapped as 'invalid request: <cause>'. Unlike the earlier scheme/host checks, this catches deeper validation performed by net/http itself.","triggerScenarios":"method not a valid HTTP token (e.g. contains lowercase-with-space or control chars), rawURL that url.Parse accepted earlier but NewRequest rejects, or an invalid body reader combination.","commonSituations":"LLM agents inventing custom methods (`GETS`, `DELETE /x`); method strings polluted with whitespace from config; method passed empty; URL with characters net/http refuses in the request line.","solutions":["Use only standard methods: GET, POST, PUT, DELETE, PATCH (uppercase, no spaces)","Trim/validate the method string before calling HTTPRequest","Re-check the URL for illegal characters or malformed percent-encoding","Wrap the call and log err.Error() to see the precise net/http cause"],"exampleFix":"// before\nHTTPRequest(\"get /v1\", url, nil, \"\")\n// after\nHTTPRequest(\"GET\", url, nil, \"\")","handlingStrategy":"try-catch","validationCode":"validMethods := map[string]bool{\"GET\": true, \"POST\": true, \"PUT\": true, \"DELETE\": true, \"PATCH\": true}\nif !validMethods[method] {\n    return fmt.Errorf(\"unsupported method %q\", method)\n}","typeGuard":"func isStandardMethod(m string) bool {\n    switch m {\n    case \"GET\", \"POST\", \"PUT\", \"DELETE\", \"PATCH\", \"HEAD\", \"OPTIONS\":\n        return true\n    }\n    return false\n}","tryCatchPattern":"if _, _, _, err := HTTPRequest(method, url, nil, \"\"); err != nil {\n    if strings.HasPrefix(err.Error(), \"invalid request: \") {\n        // log err.Error(): usually a bad method token or malformed URL\n    }\n    return err\n}","preventionTips":["Restrict method input to a fixed whitelist of standard verbs","Trim and uppercase method strings from config/LLM output","Validate the full URL before passing it on","Log the wrapped net/http cause to pinpoint the invalid field"],"tags":["http","validation","request"],"backgroundTag":"invalid-argument-value","analyzedSha":"8641553a1f07374001902d3ce773285db1292b2d","analyzedAt":"2026-09-11T16:08:28.414Z","contentChangedAt":"2026-09-11T16:08:28.414Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}