{"record":{"id":"4f91379db748d299","repo":"nocobase/nocobase","slug":"chart-query-sorting-does-not-support-aggregated-me","errorCode":null,"errorMessage":"chart query.sorting does not support aggregated measure outputs or custom measure aliases in builder mode","messagePattern":"chart query\\.sorting does not support aggregated measure outputs or custom measure aliases in builder mode","errorType":"validation","errorClass":"FlowSurfaceBadRequestError","httpStatus":400,"severity":"error","filePath":"packages/plugins/@nocobase/plugin-flow-engine/src/server/flow-surfaces/chart-config.ts","lineNumber":1211,"sourceCode":"    return;\n  }\n\n  const unsupportedMeasureOutputs = new Set<string>();\n  for (const measure of _.castArray(query?.measures || [])) {\n    const outputAlias = aliasOfSelection(measure);\n    const rawFieldAlias = aliasOfFieldValue(measure?.field);\n    if (!outputAlias) {\n      continue;\n    }\n    if (measure?.aggregation || (rawFieldAlias && outputAlias !== rawFieldAlias)) {\n      unsupportedMeasureOutputs.add(outputAlias);\n    }\n  }\n\n  for (const item of sorting) {\n    const sortingField = aliasOfFieldValue(item?.field);\n    if (sortingField && unsupportedMeasureOutputs.has(sortingField)) {\n      throw new FlowSurfaceBadRequestError(\n        'chart query.sorting does not support aggregated measure outputs or custom measure aliases in builder mode',\n      );\n    }\n  }\n}\n\nfunction normalizeBuilderQuery(query: Record<string, any>) {\n  const resource = normalizeMergedChartResource(query, { required: true });\n  const dimensions = _.isUndefined(query.dimensions)\n    ? undefined\n    : Array.isArray(query.dimensions)\n      ? query.dimensions.map((item, index) => normalizeChartDimension(item, index))\n      : (() => {\n          throw new FlowSurfaceBadRequestError('chart query.dimensions must be an array');\n        })();\n  const measures = Array.isArray(query.measures)\n    ? query.measures\n        .map((item, index) => normalizeChartMeasure(item, index))","sourceCodeStart":1193,"sourceCodeEnd":1229,"githubUrl":"https://github.com/nocobase/nocobase/blob/fa42722fefe44265490dff2c27d79e2882bce4fa/packages/plugins/@nocobase/plugin-flow-engine/src/server/flow-surfaces/chart-config.ts#L1193-L1229","documentation":"In builder mode, runtime sorting cannot target aggregated measure outputs or custom measure aliases because the aggregation happens after row-level data is fetched, so the runtime cannot order by those computed columns. The validator builds a set of unsupported measure output aliases (unsupportedMeasureOutputs) and rejects any sorting item referencing them.","triggerScenarios":"A builder-mode chart query with measures that aggregate (sum/count/avg etc.) or define custom aliases, combined with query.sorting whose field alias matches one of those aggregated/custom measure outputs.","commonSituations":"Trying to sort a bar chart descending by a summed revenue measure; sorting by a count(*) alias; templates migrated from SQL-mode charts where ORDER BY on aggregates was legal.","solutions":["Remove the sorting entry that references the aggregated measure and sort client-side after data retrieval.","Sort by a dimension instead (e.g. by the category/date field) if the business goal allows.","If aggregate sorting is essential, switch the chart query to mode='sql' where ORDER BY on aggregates is supported."],"exampleFix":"// before\nmeasures: [{ field: 'amount', aggregate: 'sum', alias: 'total' }], sorting: [{ field: 'total' }]\n// after\nmeasures: [{ field: 'amount', aggregate: 'sum', alias: 'total' }]\n// no sorting; sort rows in the client after fetching","handlingStrategy":"validation","validationCode":"function validateNoAggregatedSorting(query) {\n  const aggregatedAliases = new Set(\n    query.measures.filter((m) => m.aggregate || m.alias !== m.field).map((m) => m.alias || m.field),\n  );\n  for (const item of query.sorting || []) {\n    if (aggregatedAliases.has(item.field)) {\n      throw new Error(`cannot sort by aggregated measure '${item.field}' in builder mode`);\n    }\n  }\n}","typeGuard":"function isSortableMeasure(m: { aggregate?: string; alias?: string; field: string }): boolean {\n  return !m.aggregate && (m.alias ?? m.field) === m.field;\n}","tryCatchPattern":"try {\n  return await runChartQuery(query);\n} catch (err) {\n  if (err?.message?.includes('aggregated measure outputs')) {\n    const safe = { ...query, sorting: (query.sorting || []).filter((s) => !isMeasureAlias(query, s.field)) };\n    return runChartQuery(safe);\n  }\n  throw err;\n}","preventionTips":["Only offer dimension fields in sort dropdowns for builder-mode charts.","Sort aggregated results client-side after fetching when descending-by-total is needed.","Switch to mode='sql' explicitly when ORDER BY on aggregates is a hard requirement."],"tags":["validation","chart","sorting","aggregation"],"backgroundTag":"invalid-sort-field","analyzedSha":"fa42722fefe44265490dff2c27d79e2882bce4fa","analyzedAt":"2026-09-01T00:54:31.202Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}