{"record":{"id":"f695ce88f2948d45","repo":"SigNoz/signoz","slug":"error-decoding-request-v","errorCode":null,"errorMessage":"error decoding request: %v","messagePattern":"error decoding request: (.+?)","errorType":"http","errorClass":"model.ApiError","httpStatus":400,"severity":"error","filePath":"pkg/query-service/app/http_handler.go","lineNumber":4437,"sourceCode":"\n\tchq, err := traceFunnelsModule.GetErroredTraces(funnel, req.TimeRange, req.StepStart, req.StepEnd)\n\tif err != nil {\n\t\tRespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: fmt.Errorf(\"error building clickhouse query: %v\", err)}, nil)\n\t\treturn\n\t}\n\n\tresults, err := aH.reader.GetListResultV3(r.Context(), chq.Query)\n\tif err != nil {\n\t\tRespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: fmt.Errorf(\"error converting clickhouse results to list: %v\", err)}, nil)\n\t\treturn\n\t}\n\taH.Respond(w, results)\n}\n\nfunc (aH *APIHandler) handleValidateTracesWithPayload(w http.ResponseWriter, r *http.Request) {\n\tvar req traceFunnels.PostableFunnel\n\tif err := json.NewDecoder(r.Body).Decode(&req); err != nil {\n\t\tRespondError(w, &model.ApiError{Typ: model.ErrorBadData, Err: fmt.Errorf(\"error decoding request: %v\", err)}, nil)\n\t\treturn\n\t}\n\n\tif len(req.Steps) < 2 {\n\t\tRespondError(w, &model.ApiError{Typ: model.ErrorBadData, Err: fmt.Errorf(\"funnel must have at least 2 steps\")}, nil)\n\t\treturn\n\t}\n\n\t// Create a StorableFunnel from the request\n\tfunnel := &traceFunnels.StorableFunnel{\n\t\tSteps: req.Steps,\n\t}\n\n\tchq, err := traceFunnelsModule.ValidateTraces(funnel, traceFunnels.TimeRange{\n\t\tStartTime: req.StartTime,\n\t\tEndTime:   req.EndTime,\n\t})\n\tif err != nil {","sourceCodeStart":4419,"sourceCodeEnd":4455,"githubUrl":"https://github.com/SigNoz/signoz/blob/5069bf80b08f1f00d7e014eccc09902f9871004f/pkg/query-service/app/http_handler.go#L4419-L4455","documentation":"Thrown when the ad-hoc funnel validation endpoint cannot decode its JSON body into traceFunnels.PostableFunnel. It is an ErrorBadData (HTTP 400): the client sent a body that doesn't match the postable funnel schema (steps array, startTime, endTime).","triggerScenarios":"POSTing to the validate endpoint with a body missing the steps array, steps with wrong field types, invalid JSON syntax, or an empty body.","commonSituations":"Frontend sending a draft funnel with null steps, API consumers guessing field names, or a proxy/gateway truncating large bodies with many steps.","solutions":["Match the PostableFunnel schema exactly (steps[].service, steps[].span, startTime, endTime as epoch ms)","Send Content-Type: application/json with a single JSON object","Validate the payload client-side before submission"],"exampleFix":"// before\ncurl -X POST /api/v1/funnels/validate -d '{\"steps\": []}'\n// after\ncurl -X POST /api/v1/funnels/validate -H 'Content-Type: application/json' \\\n  -d '{\"steps\":[{\"service\":\"frontend\",\"span\":\"GET /cart\"},{\"service\":\"billing\",\"span\":\"POST /charge\"}],\"startTime\":1700000000000,\"endTime\":1700086400000}'","handlingStrategy":"validation","validationCode":"const draft = { steps, startTime, endTime };\nif (!Array.isArray(steps) || steps.some(s => !s.service || !s.span)) throw new TypeError('each step needs service and span');\nif (!(startTime < endTime)) throw new RangeError('invalid time range');","typeGuard":"function isPostableFunnel(b: unknown): b is PostableFunnel { const r = b as any; return Array.isArray(r?.steps) && r.steps.length >= 2 && typeof r.startTime === 'number' && typeof r.endTime === 'number'; }","tryCatchPattern":"try { await api.post('/funnels/validate', draft); } catch (e) { if (e.status === 400) showValidationMessage(e.data.error); else throw e; }","preventionTips":["Disable submit until schema checks pass","Use shared types generated from the Go structs","Test with curl to rule out transport mangling"],"tags":["signoz","funnel","json-decode","bad-request"],"backgroundTag":"request-body-validation-failed","analyzedSha":"5069bf80b08f1f00d7e014eccc09902f9871004f","analyzedAt":"2026-08-28T06:22:12.824Z","schemaVersion":2},"datasetVersion":"2026-08-28T11:17:15.048Z"}