{"record":{"id":"d374f4b3cb4878cb","repo":"apache/beam","slug":"empty-request-body","errorCode":null,"errorMessage":"empty request body","messagePattern":"empty request body","errorType":"http","errorClass":null,"httpStatus":400,"severity":"error","filePath":"sdks/go/pkg/beam/runners/prism/internal/web/web.go","lineNumber":394,"sourceCode":"\ntype cancelJobRequest struct {\n\tJobID string `json:\"job_id\"`\n}\n\nfunc (h *jobCancelHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {\n\tvar cancelRequest *cancelJobRequest\n\tif r.Method != http.MethodPost {\n\t\thttp.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)\n\t\treturn\n\t}\n\tbody, err := io.ReadAll(r.Body)\n\tif err != nil {\n\t\terr = fmt.Errorf(\"could not read request body: %w\", err)\n\t\thttp.Error(w, err.Error(), http.StatusBadRequest)\n\t\treturn\n\t}\n\tif len(body) == 0 {\n\t\thttp.Error(w, \"empty request body\", http.StatusBadRequest)\n\t\treturn\n\t}\n\tif err := json.Unmarshal(body, &cancelRequest); err != nil {\n\t\terr = fmt.Errorf(\"error parsing JSON: %s of request: %w\", body, err)\n\t\thttp.Error(w, err.Error(), http.StatusBadRequest)\n\t\treturn\n\t}\n\n\t// Forward JobId from POST body avoids direct json Unmarshall on composite types containing protobuf message types.\n\tresp, err := h.Jobcli.Cancel(r.Context(), &jobpb.CancelJobRequest{\n\t\tJobId: cancelRequest.JobID,\n\t})\n\tif err != nil {\n\t\tstatusCode := status.Code(err)\n\t\thttpCode := http.StatusInternalServerError\n\t\tif c, ok := grpcToHttpCodes[statusCode]; ok {\n\t\t\thttpCode = c\n\t\t}","sourceCodeStart":376,"sourceCodeEnd":412,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/go/pkg/beam/runners/prism/internal/web/web.go#L376-L412","documentation":"HTTP 400 error returned by the Prism web UI's jobCancelHandler when a POST /jobs/cancel-style request has a completely empty body. It is caught after reading the body succeeds but before JSON parsing, distinguishing 'nothing sent' from 'malformed JSON sent'.","triggerScenarios":"POSTing to the cancel endpoint with zero-length body, e.g. curl -X POST with no -d/--data option.","commonSituations":"curl calls that forget the -d flag; HTTP clients that send POST without a body; tests hitting the endpoint without payloads.","solutions":["Resend the POST with a JSON body containing job_id","Ensure the client serializes the request body before sending","Set Content-Type: application/json on the request"],"exampleFix":"// before\ncurl -X POST http://localhost:8083/jobs/cancel\n// after\ncurl -X POST -H 'Content-Type: application/json' -d '{\"job_id\":\"job-123\"}' http://localhost:8083/jobs/cancel","handlingStrategy":"validation","validationCode":"payload := fmt.Sprintf(`{\"job_id\":%q}`, jobID)\nif len(payload) == 0 { return errors.New(\"cancel payload is empty\") }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always serialize a JSON body before POSTing","Never send bare -X POST without -d in curl"],"tags":["http","validation","empty-body"],"backgroundTag":"empty-required-field","analyzedSha":"12126d8942aaf848030c478b4c6a28c6af861c66","analyzedAt":"2026-09-13T01:50:10.254Z","contentChangedAt":"2026-09-13T01:50:10.254Z","schemaVersion":2},"datasetVersion":"2026-09-20T03:17:13.778Z"}