{"record":{"id":"3a32f5f8fad2e6bb","repo":"TanStack/table","slug":"aggregationfn-at-index-i-for-column-column-i","errorCode":null,"errorMessage":"aggregationFn at index ${i} for column '${column.id}' needs a stable id","messagePattern":"aggregationFn at index (.+?) for column '(.+?)' needs a stable id","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"packages/table-core/src/features/row-aggregation/rowAggregationFeature.utils.ts","lineNumber":300,"sourceCode":"        : isAggregationFnDescriptor(item)\n          ? item.id\n          : undefined\n    if (id !== undefined) ids[id] = (ids[id] ?? 0) + 1\n  }\n\n  const resolved: Array<ResolvedAggregationFn<TFeatures, TData>> = []\n\n  for (let i = 0; i < option.length; i++) {\n    const item = option[i]\n    const id =\n      typeof item === 'string'\n        ? item\n        : isAggregationFnDescriptor(item)\n          ? item.id\n          : undefined\n\n    if (id === undefined) {\n      warn(\n        `aggregationFn at index ${i} for column '${column.id}' needs a stable id`,\n      )\n      resolved.push({ aggregationFn: undefined, id: undefined })\n      continue\n    }\n\n    if (ids[id]! > 1) {\n      warn(`aggregationFn id '${id}' for column '${column.id}' is duplicated`)\n      resolved.push({ aggregationFn: undefined, id })\n      continue\n    }\n\n    const ref = isAggregationFnDescriptor(item) ? item.aggregationFn : item\n    resolved.push({\n      aggregationFn: resolveAggregationFn(column, ref),\n      id,\n    })\n  }","sourceCodeStart":282,"sourceCodeEnd":318,"githubUrl":"https://github.com/TanStack/table/blob/d01c01bedbab0ff6c2641f18b2fc9a11545d9bf6/packages/table-core/src/features/row-aggregation/rowAggregationFeature.utils.ts#L282-L318","documentation":"When `columnDef.aggregationFn` is an array, each element must be resolvable to a stable id: either a plain string (used as both ref and id) or an object descriptor carrying `id` and `aggregationFn`. Bare function values or malformed objects have no id, so this warning fires and the entry is resolved as `{ aggregationFn: undefined, id: undefined }` — it is skipped in the keyed aggregation result. The id is required so aggregated results can be keyed and merged across grouped sub-rows.","triggerScenarios":"Passing `aggregationFn: [myFn, otherFn]` (bare functions, no wrapping descriptor); passing an object with only `aggregationFn` but no `id` key (or misspelled `id`); passing a non-string, non-descriptor value like a number inside the array.","commonSituations":"Migrating from a scalar `aggregationFn` to multiple aggregations and assuming plain functions work in arrays; building descriptors dynamically and omitting `id`; TypeScript's looser typing letting an object missing `id` slip through with `as any`.","solutions":["Wrap each bare function in a descriptor with an explicit id: `{ id: 'sum', aggregationFn: myFn }`.","Or use plain strings referencing registered fns: `aggregationFn: ['sum', 'mean']`.","Check each array element for a literal `id` property; fix typos like `key` or `name`.","Verify the multiple-aggregation result shape: entries without ids never appear in the keyed object, so add ids to see their values.","Strengthen types (drop `as any`) so the compiler enforces the `AggregationFnDescriptor` shape."],"exampleFix":"// before\naggregationFn: [sumFn, meanFn]\n\n// after\naggregationFn: [\n  { id: 'sum', aggregationFn: sumFn },\n  { id: 'mean', aggregationFn: meanFn },\n]","handlingStrategy":"validation","validationCode":"function assertStableIds(option: unknown) {\n  if (!Array.isArray(option)) return\n  option.forEach((item, i) => {\n    const ok =\n      typeof item === 'string' ||\n      (!!item && typeof item === 'object' && typeof (item as any).id === 'string')\n    if (!ok) throw new Error(`aggregationFn[${i}] must be a string or { id, aggregationFn }`)\n  })\n}\nassertStableIds(columnDef.aggregationFn)","typeGuard":"function isAggregationFnDescriptor(\n  value: unknown,\n): value is { id: string; aggregationFn: unknown } {\n  return (\n    !!value &&\n    typeof value === 'object' &&\n    typeof (value as any).id === 'string' &&\n    'aggregationFn' in (value as any)\n  )\n}","tryCatchPattern":"try {\n  const entries = column.getAggregationFns()\n  const missing = entries.filter((e) => e.id === undefined)\n  if (missing.length) {\n    console.warn(`${missing.length} aggregation entries skipped for lack of a stable id`)\n  }\n} catch (e) {\n  console.error('failed to resolve aggregation entries', e)\n}","preventionTips":["Never put bare functions in an array-valued aggregationFn; always wrap in { id, aggregationFn }.","Keep ids unique per column and stable across renders (module-level constants, not inline literals in render).","Enable strict TypeScript typing on columnDefs so descriptors missing `id` are rejected at compile time.","Render-time check: in dev, assert every entry from getAggregationFns() has an id when aggregationFn is an array."],"tags":["aggregation","missing-identifier","configuration","dev-warning"],"backgroundTag":"missing-required-identifier","analyzedSha":"d01c01bedbab0ff6c2641f18b2fc9a11545d9bf6","analyzedAt":"2026-08-28T21:52:44.679Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}