SigNoz/signoz · error · model.ApiError
error decoding request: %v
Error message
error decoding request: %v
What it means
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).
Source
Thrown at pkg/query-service/app/http_handler.go:4437
chq, err := traceFunnelsModule.GetErroredTraces(funnel, req.TimeRange, req.StepStart, req.StepEnd)
if err != nil {
RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: fmt.Errorf("error building clickhouse query: %v", err)}, nil)
return
}
results, err := aH.reader.GetListResultV3(r.Context(), chq.Query)
if err != nil {
RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: fmt.Errorf("error converting clickhouse results to list: %v", err)}, nil)
return
}
aH.Respond(w, results)
}
func (aH *APIHandler) handleValidateTracesWithPayload(w http.ResponseWriter, r *http.Request) {
var req traceFunnels.PostableFunnel
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
RespondError(w, &model.ApiError{Typ: model.ErrorBadData, Err: fmt.Errorf("error decoding request: %v", err)}, nil)
return
}
if len(req.Steps) < 2 {
RespondError(w, &model.ApiError{Typ: model.ErrorBadData, Err: fmt.Errorf("funnel must have at least 2 steps")}, nil)
return
}
// Create a StorableFunnel from the request
funnel := &traceFunnels.StorableFunnel{
Steps: req.Steps,
}
chq, err := traceFunnelsModule.ValidateTraces(funnel, traceFunnels.TimeRange{
StartTime: req.StartTime,
EndTime: req.EndTime,
})
if err != nil {View on GitHub (pinned to 5069bf80b0)
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
Example fix
// before
curl -X POST /api/v1/funnels/validate -d '{"steps": []}'
// after
curl -X POST /api/v1/funnels/validate -H 'Content-Type: application/json' \
-d '{"steps":[{"service":"frontend","span":"GET /cart"},{"service":"billing","span":"POST /charge"}],"startTime":1700000000000,"endTime":1700086400000}' Defensive patterns
Strategy: validation
Validate before calling
const draft = { steps, startTime, endTime };
if (!Array.isArray(steps) || steps.some(s => !s.service || !s.span)) throw new TypeError('each step needs service and span');
if (!(startTime < endTime)) throw new RangeError('invalid time range'); Type guard
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'; } Try / catch
try { await api.post('/funnels/validate', draft); } catch (e) { if (e.status === 400) showValidationMessage(e.data.error); else throw e; } Prevention
- Disable submit until schema checks pass
- Use shared types generated from the Go structs
- Test with curl to rule out transport mangling
When it happens
Trigger: POSTing to the validate endpoint with a body missing the steps array, steps with wrong field types, invalid JSON syntax, or an empty body.
Common situations: Frontend sending a draft funnel with null steps, API consumers guessing field names, or a proxy/gateway truncating large bodies with many steps.
Related errors
- invalid request body: %v
- couldn't JSON decode existingFilter: %w
- cannot parse the request body: %v
- error while getting ttl. ttl type should be metrics|traces,
- ErrorID missing from params
AI-assisted analysis of SigNoz/signoz@5069bf80b0 (2026-08-28).
Data as JSON: /api/errors/f695ce88f2948d45.
Report an issue: GitHub.