SigNoz/signoz · error

select columns cannot be empty for panelType %s

Error message

select columns cannot be empty for panelType %s

What it means

buildTracesQuery (v4) with a no-op aggregate and panelType=list constructs SELECT ... <selectColumns> from the span index; when SelectColumns is empty there is nothing to project besides the fixed columns, so the request is rejected.

Source

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

				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)
				if mq.Offset != 0 {
					afterSubQuery = tracesV3.AddOffsetToQuery(afterSubQuery, mq.Offset)
				}
				query = fmt.Sprintf(constants.TraceExplorerSpanCountBeforeSubQuery, constants.SIGNOZ_TRACE_DBNAME, constants.SIGNOZ_SPAN_INDEX_V3) + withSubQuery + ") " + fmt.Sprintf(afterSubQuery, constants.SIGNOZ_TRACE_DBNAME, constants.SIGNOZ_SPAN_INDEX_V3, timeFilter)
			}
			// adding this to avoid the distributed product mode error which doesn't allow global in
			query += " settings distributed_product_mode='allow', max_memory_usage=10000000000"
		} else if panelType == v3.PanelTypeList {
			if len(mq.SelectColumns) == 0 {
				return "", fmt.Errorf("select columns cannot be empty for panelType %s", panelType)
			}
			selectLabels = getSelectLabels(mq.SelectColumns)
			// add it to the select labels
			queryNoOpTmpl := fmt.Sprintf("SELECT timestamp as timestamp_datetime, span_id as spanID, trace_id as traceID,%s ", selectLabels) + "from " + constants.SIGNOZ_TRACE_DBNAME + "." + constants.SIGNOZ_SPAN_INDEX_V3 + " where %s %s" + "%s"
			query = fmt.Sprintf(queryNoOpTmpl, timeFilter, filterSubQuery, orderBy)
		} else {
			return "", fmt.Errorf("unsupported aggregate operator %s for panelType %s", mq.AggregateOperator, panelType)
		}
		return query, nil
		// ---- NOOP ends here ----
	}

	having := tracesV3.Having(mq.Having)
	if having != "" {
		having = " having " + having
	}

	groupBy := tracesV3.GroupByAttributeKeyTags(panelType, options.GraphLimitQtype, mq.GroupBy...)

View on GitHub (pinned to 5069bf80b0)

Solutions

  1. Populate mq.SelectColumns with the attribute keys to display (e.g. service.name, name, duration_ns)
  2. Default to a sensible projection like [service.name, name, responseStatusCode] if the user selected none

Example fix

// before
mq.SelectColumns = []v3.AttributeKey{}

// after
mq.SelectColumns = []v3.AttributeKey{{Key:"service.name",DataType:"string",Type:"resource"}, {Key:"name",DataType:"string",Type:"tag"}}
Defensive patterns

Strategy: validation

Validate before calling

if panelType == v3.PanelTypeList && mq.AggregateOperator == v3.AggregateOperatorNoOp && len(mq.SelectColumns) == 0 {
    mq.SelectColumns = []v3.AttributeKey{{Key: "service.name", DataType: "string", Type: "resource"}}
}

Type guard

func hasSelectColumns(mq *v3.BuilderQuery) bool { return len(mq.SelectColumns) > 0 }

Prevention

When it happens

Trigger: Sending a PanelTypeList no-op traces query with an empty or missing mq.SelectColumns array.

Common situations: Programmatic list queries that only set filters and forget projections; UI bugs dropping select columns during query serialization.

Related errors


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