TanStack/table · error
coreColumnsFeature require an id when using a non-string hea
Error message
coreColumnsFeature require an id when using a non-string header
What it means
constructColumn throws this when a column definition has no id and its header is not a plain string, so the library cannot infer an id (only string headers or accessorKey yield an id). It is the non-accessorFn branch of the same missing-id guard, in development mode.
Source
Thrown at packages/table-core/src/core/columns/constructColumn.ts:94
result = result?.[key]
if (process.env.NODE_ENV === 'development' && result === undefined) {
console.warn(
`"${key}" in deeply nested key "${accessorKey}" returned undefined.`,
)
}
}
return result as TValue
}
} else {
accessorFn = (originalRow: TData) =>
(originalRow as any)[resolvedColumnDef.accessorKey]
}
}
if (!id) {
if (process.env.NODE_ENV === 'development') {
throw new Error(
resolvedColumnDef.accessorFn
? `coreColumnsFeature require an id when using an accessorFn`
: `coreColumnsFeature require an id when using a non-string header`,
)
}
throw new Error()
}
// Create column with shared prototype for memory efficiency
const columnPrototype = getColumnPrototype(table)
const column = Object.create(columnPrototype) as Column_CoreProperties<
TFeatures,
TData,
TValue
>
// Only assign instance-specific properties
column.accessorFn = accessorFnView on GitHub (pinned to d01c01bedb)
Solutions
- Add an explicit id to the column definition.
- If the column needs no accessor, still give it a stable id like 'actions'.
- Keep headers as plain strings where possible so ids can be inferred.
Example fix
// before
{ header: (props) => <ActionsMenu {...props} /> }
// after
{ id: 'actions', header: (props) => <ActionsMenu {...props} /> } Defensive patterns
Strategy: validation
Validate before calling
function assertColumnIdOrHeader(def) {
const inferable = typeof def.id === 'string' || typeof def.header === 'string' || typeof def.accessorKey === 'string'
if (!inferable) throw new Error('Column needs an explicit id (non-string header, no accessorKey)')
}
columns.forEach(assertColumnIdOrHeader) Type guard
function canInferColumnId(def: ColumnDef<any>): boolean {
return typeof def.id === 'string' || typeof def.header === 'string' || typeof def.accessorKey === 'string'
} Try / catch
try {
table.getColumn('actions') // built via constructColumn
} catch (e) {
if ((e as Error).message.includes('require an id')) {
console.error('Display-only column missing id:', e)
} else throw e
} Prevention
- Give every display-only column (actions, selection, drag) an explicit id
- Add a dev-time column config validator in your app
- When converting string headers to custom components, add id in the same change
When it happens
Trigger: Passing a columnDef with a non-string header (component/JSX/render function) and no accessorKey/accessorFn and no id, in development.
Common situations: Display-only columns with custom render headers (e.g. row actions, drag handles) that omit id; migrating columns from string headers to custom header components.
Related errors
- coreColumnsFeature require an id when using an accessorFn
- "${key}" in deeply nested key "${accessorKey}" returned unde
- [Table] Column with id '${columnId}' does not exist.
- filterFn '${filterFnName}' (auto) for column '${column.id}'
- filterFn '${String(column.columnDef.filterFn)}' for column '
AI-assisted analysis of TanStack/table@d01c01bedb (2026-08-28).
Data as JSON: /api/errors/49752dbdf1458526.
Report an issue: GitHub.