{"record":{"id":"86fd435a11c4a8d9","repo":"air-verse/air","slug":"proxy-handler-bad-form","errorCode":null,"errorMessage":"proxy handler: bad form","messagePattern":"proxy handler: bad form","errorType":"http","errorClass":null,"httpStatus":500,"severity":"warning","filePath":"runner/proxy.go","lineNumber":157,"sourceCode":"\tpage := buf.String()\n\n\t// the script will be injected before the end of the body tag. In case the tag is missing, the injection will be skipped with no error.\n\tbody := strings.LastIndex(page, \"</body>\")\n\tif body == -1 {\n\t\treturn page, decoded, nil\n\t}\n\n\tscript := \"<script>\" + ProxyScript + \"</script>\"\n\treturn page[:body] + script + page[body:], decoded, nil\n}\n\nfunc (p *Proxy) proxyHandler(w http.ResponseWriter, r *http.Request) {\n\tappURL := r.URL\n\tappURL.Scheme = \"http\"\n\tappURL.Host = fmt.Sprintf(\"localhost:%d\", p.config.AppPort)\n\n\tif err := r.ParseForm(); err != nil {\n\t\thttp.Error(w, \"proxy handler: bad form\", http.StatusInternalServerError)\n\t\treturn\n\t}\n\tvar body io.Reader\n\tif len(r.Form) > 0 {\n\t\tbody = strings.NewReader(r.Form.Encode())\n\t} else {\n\t\tbody = r.Body\n\t}\n\treq, err := http.NewRequest(r.Method, appURL.String(), body)\n\tif err != nil {\n\t\thttp.Error(w, \"proxy handler: unable to create request\", http.StatusInternalServerError)\n\t\treturn\n\t}\n\n\t// Copy the headers from the original request\n\tfor name, values := range r.Header {\n\t\tfor _, value := range values {\n\t\t\treq.Header.Add(name, value)","sourceCodeStart":139,"sourceCodeEnd":175,"githubUrl":"https://github.com/air-verse/air/blob/71ea1dee05248122ed22b5870c7fb20a90d80ddd/runner/proxy.go#L139-L175","documentation":"proxyHandler must parse the incoming request's form data so it can re-encode the body when forwarding. If r.ParseForm fails (malformed query string or form body), the proxy responds with HTTP 500 and this fixed message.","triggerScenarios":"A client sends a request to the air proxy whose URL query string or application/x-www-form-urlencoded body is malformed (e.g. bad percent-encoding like `%zz`), causing http.Request.ParseForm to return an error.","commonSituations":"Frontend code building query strings with improperly escaped special characters (% not encoded as %25); manually constructed URLs with raw `#`, spaces, or broken percent-encoding; test tools sending deliberately bad forms.","solutions":["URL-encode query parameters properly in the client (encodeURIComponent / url.Values.Encode)","Fix malformed percent-escapes in the request URL (e.g. use %25 for a literal %)","Reproduce the failing URL and check its query string with url.ParseRequestURI","Send a JSON body with correct Content-Type instead of a form if that suits the API"],"exampleFix":"// before (client)\nfetch('/api?q=100%')\n// after\nfetch('/api?q=' + encodeURIComponent('100%'))","handlingStrategy":"validation","validationCode":"// validate outgoing URLs in the client\nu, err := url.Parse(rawURL)\nif err != nil || strings.Contains(u.RawQuery, \"%\") {\n\t// ensure every % is part of a valid escape\n\tif err != nil || regexp.MustCompile(`%(?![0-9A-Fa-f]{2})`).MatchString(u.RawQuery) {\n\t\treturn fmt.Errorf(\"malformed query string: %q\", rawURL)\n\t}\n}","typeGuard":null,"tryCatchPattern":"resp, err := http.Get(url)\nif err != nil && resp != nil && resp.StatusCode == 500 {\n\tbody, _ := io.ReadAll(resp.Body)\n\tif strings.Contains(string(body), \"proxy handler: bad form\") {\n\t\t// fix percent-encoding in the request URL/body\n\t}\n}","preventionTips":["Always encodeURIComponent (JS) or url.Values.Encode (Go) query params","Escape literal % as %25 in URLs","Never build query strings by string concatenation","Sanitize user input before putting it in URLs"],"tags":["proxy","http","request-parsing"],"backgroundTag":"malformed-query-string","analyzedSha":"71ea1dee05248122ed22b5870c7fb20a90d80ddd","analyzedAt":"2026-08-31T20:27:54.676Z","schemaVersion":2},"datasetVersion":"2026-09-01T03:17:15.561Z"}