{"record":{"id":"e3822374688c179f","repo":"shadow1ng/fscan","slug":"webscan-response-body-process-failed","errorCode":"webscan_response_body_process_failed","errorMessage":"webscan_response_body_process_failed: %w","messagePattern":"webscan_response_body_process_failed: %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"webscan/lib/Eval.go","lineNumber":651,"sourceCode":"\t} else {\n\t\trespURL = &UrlType{}\n\t}\n\tresp := Response{\n\t\tStatus:      int32(oResp.StatusCode),\n\t\tURL:         respURL,\n\t\tHeaders:     make(map[string]string),\n\t\tContentType: oResp.Header.Get(\"Content-Type\"),\n\t}\n\n\t// 复制响应头，合并多值头部为分号分隔的字符串\n\tfor k := range oResp.Header {\n\t\tresp.Headers[k] = strings.Join(oResp.Header.Values(k), \";\")\n\t}\n\n\t// 读取并解析响应体\n\tbody, err := getRespBody(oResp)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"%s: %w\", i18n.GetText(\"webscan_response_body_process_failed\"), err)\n\t}\n\tresp.Body = body\n\n\treturn &resp, nil\n}\n\n// getRespBody 读取 HTTP 响应体并处理可能的 gzip 压缩\nfunc getRespBody(oResp *http.Response) ([]byte, error) {\n\t// 读取原始响应体\n\tbody, err := io.ReadAll(io.LimitReader(oResp.Body, maxPOCResponseBodyBytes))\n\tif err != nil && !errors.Is(err, io.EOF) && len(body) == 0 {\n\t\treturn nil, err\n\t}\n\n\t// 处理 gzip 压缩\n\tif strings.Contains(oResp.Header.Get(\"Content-Encoding\"), \"gzip\") {\n\t\treader, err := gzip.NewReader(bytes.NewReader(body))\n\t\tif err != nil {","sourceCodeStart":633,"sourceCodeEnd":669,"githubUrl":"https://github.com/shadow1ng/fscan/blob/95cc12e753bf43de7004e5aef42a9ffba3934303/webscan/lib/Eval.go#L633-L669","documentation":"ParseResponse reads the HTTP response body via getRespBody while converting an http.Response into the scanner's response struct. If reading the body fails, the error is wrapped as webscan_response_body_process_failed. Called from DoRequest, so any failed POC response body read surfaces through this error.","triggerScenarios":"getRespBody(oResp) returns an error: response body reader errors mid-read (connection reset while streaming body), truncated/chunked transfer errors, or a body already closed before parsing.","commonSituations":"Target closes the connection before the full body arrives; gzip-encoded bodies that fail to decompress inside getRespBody; very large responses hitting read limits; flaky mobile/GMTLS connections.","solutions":["Unwrap the error to find the underlying read/decompression failure.","Retry the request; transient connection resets during body reads are common and the scan state already counts TCP success/failure.","Increase read timeouts so the body stream is not cut off mid-transfer.","Check whether the response uses an encoding (gzip/brotli) that getRespBody handles."],"exampleFix":"// before\nresp, err := DoRequest(req)\nif err != nil { return err }\n\n// after\nresp, err := DoRequest(req)\nif err != nil {\n    // includes webscan_response_body_process_failed from body reads\n    return fmt.Errorf(\"poc request failed: %w\", err)\n}","handlingStrategy":"try-catch","validationCode":"if oResp.StatusCode == 0 || oResp.Body == nil {\n    return errors.New(\"response has no readable body\")\n}","typeGuard":null,"tryCatchPattern":"parsed, err := DoRequest(req)\nif err != nil {\n    if strings.Contains(err.Error(), \"webscan_response_body_process_failed\") {\n        // retry once; body reads often fail transiently\n    }\n    return err\n}","preventionTips":["Set generous read timeouts so body streams aren't cut mid-transfer.","Close response bodies only after full parsing (DoRequest already defers Body.Close).","Retry on connection-reset style failures."],"tags":["http","response-body","io","webscan"],"backgroundTag":"invalid-json-response","analyzedSha":"95cc12e753bf43de7004e5aef42a9ffba3934303","analyzedAt":"2026-09-06T17:07:30.094Z","contentChangedAt":"2026-09-06T17:07:30.094Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}