{"record":{"id":"b956eacc1a905b6e","repo":"xai-org/grok-build","slug":"failed-to-parse-part-response","errorCode":null,"errorMessage":"Failed to parse part {} response: {}","messagePattern":"Failed to parse part (.+?) response: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/codegen/xai-file-utils/src/storage_client.rs","lineNumber":2018,"sourceCode":"            .header(\"Content-Length\", length.to_string());\n        for (name, value) in crate::trace_context::trace_context_headers().iter() {\n            request = request.header(name.clone(), value.clone());\n        }\n\n        match request.body(body).send().await {\n            Ok(response) => {\n                tracing::debug!(\n                    \"Part {} HTTP response received in {:?}, status={}\",\n                    part_number,\n                    send_start.elapsed(),\n                    response.status()\n                );\n\n                if response.status().is_success() {\n                    let parse_start = std::time::Instant::now();\n                    let part_response: MultipartUploadPartResponse =\n                        response.json().await.map_err(|e| {\n                            anyhow::anyhow!(\"Failed to parse part {} response: {}\", part_number, e)\n                        })?;\n                    tracing::debug!(\n                        \"Part {} response parsed in {:?}\",\n                        part_number,\n                        parse_start.elapsed()\n                    );\n                    return Ok(part_response);\n                }\n\n                let check = ResponseCheck::from_response(response, &operation).await;\n                if check.is_retryable && attempt < retry_config.max_retries {\n                    check.wait_for_retry(retry_config, attempt).await;\n                    attempt += 1;\n                    continue;\n                }\n                return Err(HttpUploadError {\n                    status_code: check.status_code,\n                    message: check.message,","sourceCodeStart":2000,"sourceCodeEnd":2036,"githubUrl":"https://github.com/xai-org/grok-build/blob/bc7f02eddd3d84085849dc19ed216f11c23b0571/crates/codegen/xai-file-utils/src/storage_client.rs#L2000-L2036","documentation":"During multipart part upload, a 2xx response body must decode into MultipartUploadPartResponse; when reqwest's response.json() fails, this error names the failing part number and the parse error. The part bytes reached the server, but the part acknowledgement could not be parsed, so the multipart state cannot be advanced safely.","triggerScenarios":"Part upload returning 200 with empty/HTML/garbage body (proxy interference), backend response schema drift, or truncated response due to connection issues after headers were sent.","commonSituations":"Reverse proxies timing out mid-response and emitting partial/HTML bodies; version mismatch between client expectations and server part-response format; load balancer error pages returned with 2xx-adjacent statuses.","solutions":["Log the raw body for the failing part number to identify what the server returned.","Verify proxy/load-balancer health for the multipart endpoint route.","Align client/server versions so MultipartUploadPartResponse matches the real payload.","Retry the specific part upload once the response path is fixed."],"exampleFix":"// before\nlet part_response: MultipartUploadPartResponse = response.json().await\n    .map_err(|e| anyhow::anyhow!(\"Failed to parse part {} response: {}\", part_number, e))?;\n\n// after\nlet text = response.text().await?;\nlet part_response: MultipartUploadPartResponse = serde_json::from_str(&text)\n    .map_err(|e| anyhow::anyhow!(\"Failed to parse part {} response: {} (body: {:.200})\", part_number, e, text))?;","handlingStrategy":"try-catch","validationCode":"// Ensure the multipart endpoint answers JSON before starting part uploads\nlet probe = reqwest::Client::new().post(multipart_start_url).send().await?;\nif !probe.headers().get(reqwest::header::CONTENT_TYPE)\n    .map(|v| v.to_str().unwrap_or_default().contains(\"json\")).unwrap_or(false) {\n    anyhow::bail!(\"multipart endpoint not returning JSON; check proxy/route\");\n}","typeGuard":null,"tryCatchPattern":"match client.upload_part(part_number, bytes, upload_id).await {\n    Ok(resp) => record_part(resp),\n    Err(e) if e.to_string().starts_with(\"Failed to parse part\") => {\n        let part = extract_part_number(&e.to_string());\n        tracing::error!(\"part {part} got non-JSON ack: {e:#}; retrying that part\");\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Keep multipart response schemas covered by client/server contract tests.","Configure proxies/LBs to not intercept the multipart routes.","Handle per-part retries idempotently so a failed parse can be re-sent.","Log raw bodies for part responses in debug mode to speed diagnosis."],"tags":["rust","multipart-upload","json-parsing","http","reqwest"],"backgroundTag":"invalid-json-response","analyzedSha":"bc7f02eddd3d84085849dc19ed216f11c23b0571","analyzedAt":"2026-08-31T04:59:42.031Z","schemaVersion":2},"datasetVersion":"2026-08-31T09:17:48.483Z"}