{"record":{"id":"6c788c0693794711","repo":"wavetermdev/waveterm","slug":"failed-to-read-request-body-v","errorCode":null,"errorMessage":"failed to read request body: %v","messagePattern":"failed to read request body: (.+?)","errorType":"http","errorClass":null,"httpStatus":400,"severity":"error","filePath":"tsunami/engine/serverhandlers.go","lineNumber":118,"sourceCode":"\nfunc (h *httpHandlers) handleRender(w http.ResponseWriter, r *http.Request) {\n\tdefer func() {\n\t\tpanicErr := util.PanicHandler(\"handleRender\", recover())\n\t\tif panicErr != nil {\n\t\t\thttp.Error(w, fmt.Sprintf(\"internal server error: %v\", panicErr), http.StatusInternalServerError)\n\t\t}\n\t}()\n\n\tsetNoCacheHeaders(w)\n\n\tif r.Method != http.MethodPost {\n\t\thttp.Error(w, \"method not allowed\", http.StatusMethodNotAllowed)\n\t\treturn\n\t}\n\n\tbody, err := io.ReadAll(r.Body)\n\tif err != nil {\n\t\thttp.Error(w, fmt.Sprintf(\"failed to read request body: %v\", err), http.StatusBadRequest)\n\t\treturn\n\t}\n\n\tvar feUpdate rpctypes.VDomFrontendUpdate\n\tif err := json.Unmarshal(body, &feUpdate); err != nil {\n\t\thttp.Error(w, fmt.Sprintf(\"failed to parse JSON: %v\", err), http.StatusBadRequest)\n\t\treturn\n\t}\n\n\tif feUpdate.ForceTakeover {\n\t\th.Client.clientTakeover(feUpdate.ClientId)\n\t}\n\n\tif err := h.Client.checkClientId(feUpdate.ClientId); err != nil {\n\t\thttp.Error(w, fmt.Sprintf(\"client id error: %v\", err), http.StatusBadRequest)\n\t\treturn\n\t}\n","sourceCodeStart":100,"sourceCodeEnd":136,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/tsunami/engine/serverhandlers.go#L100-L136","documentation":"handleRender reads the entire request body with io.ReadAll; a read failure returns HTTP 400 'failed to read request body: <err>' (tsunami/engine/serverhandlers.go:118). The client's request was malformed or the connection broke mid-upload.","triggerScenarios":"The HTTP body is truncated by a disconnect, a body-size limit trips, or the client aborts the POST before finishing the upload.","commonSituations":"Large render payloads over unstable connections, proxy timeouts cutting uploads short, test harnesses sending a closed/empty body reader.","solutions":["Inspect the wrapped error (unexpected EOF, body too large) to pick the right fix.","Retry the POST; transient disconnects resolve on resend.","Reduce payload size or raise body-size limits on the server/proxy.","Ensure the client fully writes the body before reading the response and does not cancel the request early."],"exampleFix":"// before: aborting mid-send\nc.abort() // while fetch body still uploading\n\n// after: await the request completion\nawait fetch(renderUrl, {method: \"POST\", body: JSON.stringify(feUpdate), signal: controller.signal})","handlingStrategy":"retry","validationCode":"const payload = JSON.stringify(feUpdate);\nif (payload.length > MAX_BODY_BYTES) throw new Error(\"render payload too large\");","typeGuard":null,"tryCatchPattern":"const resp = await fetch(renderUrl, {method: \"POST\", body: payload});\nif (resp.status === 400 && (await resp.text()).includes(\"failed to read request body\")) {\n  return retryWithBackoff(() => fetch(renderUrl, {method: \"POST\", body: payload}));\n}","preventionTips":["Keep render payloads small; batch or chunk large updates.","Do not cancel fetch requests while the body is still uploading.","Match proxy/client/server timeouts for large POSTs."],"tags":["go","http","request-body","io"],"backgroundTag":"request-body-read-failed","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}