SigNoz/signoz · error

multiple orderBy criteria are not supported for trace querie

Error message

multiple orderBy criteria are not supported for trace queries

What it means

buildTracesQuery (v4) with AggregateOperator=no-op and panelType=trace builds a single ORDER BY clause for the trace list SQL; more than one orderBy entry cannot be expressed and is rejected.

Source

Thrown at pkg/query-service/app/traces/v4/query_builder.go:314

		filterSubQuery = filterSubQuery + " AND " + spanScopeSubQuery
	}

	// timerange will be sent in epoch millisecond
	selectLabels := getSelectLabels(mq.GroupBy)
	if selectLabels != "" {
		selectLabels = selectLabels + ","
	}

	orderBy := orderByAttributeKeyTags(panelType, mq.OrderBy, mq.GroupBy)
	if orderBy != "" {
		orderBy = " order by " + orderBy
	}

	if mq.AggregateOperator == v3.AggregateOperatorNoOp {
		var query string
		if panelType == v3.PanelTypeTrace {
			if len(mq.OrderBy) > 1 {
				return "", fmt.Errorf("multiple orderBy criteria are not supported for trace queries")
			}
			orderBySpanCount := false

			// Check if orderBy contains a specific reference to span_count
			if len(mq.OrderBy) == 1 && mq.OrderBy[0].ColumnName == constants.OrderBySpanCount {
				orderBySpanCount = true
			}
			if !orderBySpanCount {
				withSubQuery := fmt.Sprintf(constants.TracesExplorerViewSQLSelectWithSubQuery, constants.SIGNOZ_TRACE_DBNAME, constants.SIGNOZ_SPAN_INDEX_V3_LOCAL_TABLENAME, timeFilter)
				afterSubQuery := tracesV3.AddLimitToQuery(constants.TracesExplorerViewSQLSelectAfterSubQuery, mq.Limit)
				if mq.Offset != 0 {
					afterSubQuery = tracesV3.AddOffsetToQuery(afterSubQuery, mq.Offset)
				}
				query = fmt.Sprintf(constants.TracesExplorerViewSQLSelectBeforeSubQuery, constants.SIGNOZ_TRACE_DBNAME, constants.SIGNOZ_SPAN_INDEX_V3, timeFilter, filterSubQuery) + withSubQuery + ")" + afterSubQuery
			} else {
				withSubQueryWithLimits := tracesV3.AddLimitToQuery(constants.TracesExplorerSpanCountWithSubQuery, mq.Limit)
				withSubQuery := fmt.Sprintf(withSubQueryWithLimits, constants.SIGNOZ_TRACE_DBNAME, constants.SIGNOZ_SPAN_INDEX_V3_LOCAL_TABLENAME, timeFilter, filterSubQuery)
				afterSubQuery := tracesV3.AddLimitToQuery(constants.TraceExplorerSpanCountAfterSubQuery, mq.Limit)

View on GitHub (pinned to 5069bf80b0)

Solutions

  1. Reduce orderBy to a single column for trace list queries
  2. Keep only the user's primary sort and drop secondary sorts client-side

Example fix

// before
mq.OrderBy = []v3.OrderBy{{ColumnName:"timestamp", Order:"desc"},{ColumnName:"duration_ns", Order:"desc"}}

// after
mq.OrderBy = []v3.OrderBy{{ColumnName:"timestamp", Order:"desc"}}
Defensive patterns

Strategy: validation

Validate before calling

if panelType == v3.PanelTypeTrace && mq.AggregateOperator == v3.AggregateOperatorNoOp && len(mq.OrderBy) > 1 {
    mq.OrderBy = mq.OrderBy[:1] // keep primary sort
}

Type guard

func isSingleOrderBy(mq *v3.BuilderQuery) bool { return len(mq.OrderBy) <= 1 }

Prevention

When it happens

Trigger: A traces list (PanelTypeTrace) no-op query whose mq.OrderBy array contains 2 or more items.

Common situations: UI sends default timestamp ordering plus a user-added sort; copying a list panel's orderBy from another panel type that allows multi-sort.

Related errors


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