Repository navigation
Description
What happened?
A builder select on a metric source accepts valueExpression, and the MCP query tools default it to "Value". Any other expression is dropped without an error. valueExpression: "Value / 86400" returns the same numbers as "Value".
On main (2bfc630), number tile, one series:
| Metric type | aggFn |
valueExpression |
Result | Expected |
|---|---|---|---|---|
| gauge (points 86400, 172800) | max |
Value |
172800 | 172800 |
| gauge | max |
Value / 86400 |
172800 | 2 |
| sum (cumulative 0, 60, 120) | sum |
Value |
120 | 120 |
| sum | sum |
Value / 60 |
120 | 2 |
The gauge path replaces the select's valueExpression with LastValue, and the sum path replaces it with Rate / last_value(Sum), so the user's expression never reaches the SQL:
hyperdx/packages/common-utils/src/core/renderChartConfig.ts
Lines 2136 to 2141 in 2bfc630
select: [ { ..._select, valueExpression: 'LastValue', aggCondition: '', // clear up the condition since the where clause is already applied at the upstream CTE }, hyperdx/packages/common-utils/src/core/renderChartConfig.ts
Lines 2393 to 2410 in 2bfc630
..._select, aggFn: 'sum', valueExpression: 'Rate', aggCondition: '', } : _select.aggFn ? { alias: 'Value', ..._select, valueExpression: 'Rate', aggCondition: '', } : { alias: 'Value', ..._select, valueExpression: 'last_value(Sum)', aggCondition: '', },
Expected, either of:
- apply it on the computed value (for example
LastValue / 86400), which allows unit conversions without a formula; or - reject a non-default
valueExpressionon metric selects, so it doesn't fail silently.
Related: #2311 (count_distinct ignores valueExpression). Workaround: a formula (A / 86400).
Steps to reproduce
Reproduces on play.hyperdx.io? Didn't try.
An integration test in the style of packages/api/src/clickhouse/__tests__/renderChartConfig.int.test.ts, on main:
- Insert two gauge points for
repro.disk.seconds(86400 and 172800, one minute apart). renderChartConfigwithdisplayType: Number,select: [{ aggFn: 'max', metricType: 'gauge', metricName: 'repro.disk.seconds', valueExpression: 'Value / 86400' }], no granularity.- The query returns 172800. The SQL selects
max(toFloat64OrDefault(toString(LastValue))).
The same with a cumulative sum (aggFn: 'sum', valueExpression: 'Value / 60') returns the same value as Value.
How are you running HyperDX?
Helm (ClickStack chart 2.1.0) with the hyperdx:2-nightly image (2.42.0 plus the Node bump from #3456), our own ClickHouse 26.2.5.45. Reproduced on main 2bfc630 with the integration test above.
Where does it show up?
The API: MCP dashboard tools (a tile saved with valueExpression: "Value / 86400" showed raw seconds). The UI chart editor goes through the same renderChartConfig; we haven't checked whether it lets you set a metric valueExpression.
Debug info
N/A.
Logs
None; no error is raised.
Written with help from an AI assistant (Claude Code). The behaviour comes from our own deployment and an integration test on main.
Activity
bcanvural commentedon Oct 31, 2026
We'd like to fix this, and there's a choice to make first.
The UI can't hit it: for metric sources, the series editor hides the value expression and sets it to
Value(ChartSeriesEditor.tsx). It comes in through the API:getMetricSelectIssuesand accept anyvalueExpression(they only default it toValue);fieldpassed together withmetricNameinto thevalueExpression(external-api/v2/charts.ts).Either way the renderer then replaces it with its own per-bucket value, so the expression has no effect.
Two possible fixes:
Value / 86400→LastValue / 86400. That's fine for linear expressions on gauges. For sums and histograms the order matters: an expression applied before the rate gives a different result from one applied after, e.g.log(Value).getMetricSelectIssuesand the v2 charts endpoint, reject a metric select whosevalueExpressionisn'tValue. The error message points to formulas (A / 86400) for math and to the tile's number format (e.g.duration, data units) for unit display.We'd go with 2. It's small, it matches what the UI already does, and formulas and number formats already cover what people use the expression for; the silent wrong number is the real problem. Which would you prefer?
fix(common-utils): skip metric points that have no recorded value (#3583