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

  1. Match the PostableFunnel schema exactly (steps[].service, steps[].span, startTime, endTime as epoch ms)
  2. Send Content-Type: application/json with a single JSON object
  3. 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

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


AI-assisted analysis of SigNoz/signoz@5069bf80b0 (2026-08-28). Data as JSON: /api/errors/f695ce88f2948d45. Report an issue: GitHub.