TanStack/table · warning
[table-worker] ${message}
Error message
[table-worker] ${message} What it means
This is the generic dev-only warning channel for the worker-backed row-model feature. `warnOnce` prefixes every message with '[table-worker]' and fires at most once per stage. The two concrete messages are (1) the worker returned no result for a stage because the worker-side `initTableWorker()` config is missing that stage's RowModel factory, and (2) a non-contiguous offload: an upstream stage (e.g. 'filtered') is on the main thread while a downstream stage (e.g. 'sorted') is offloaded, so the upstream stage is bypassed and its computation is skipped in the worker.
Source
Thrown at packages/table-core/src/worker/createWorkerRowModel.ts:65
*/
export function createWorkerRowModel(
tableWorker: TableWorker,
stage: TableWorkerStage,
) {
return <TFeatures extends TableFeatures, TData extends RowData>(
_table: Table<TFeatures, TData>,
): (() => RowModel<TFeatures, TData>) => {
const table = _table as unknown as AnyTable
// Stages register per table as their factories initialize (first read).
// The first render may therefore fire one extra coalesced request while
// the stage set grows; single-flight makes it a cheap trailing request.
getTableWorkerBridge(tableWorker, table).stages.add(stage)
let warned = false
const warnOnce = (message: string) => {
if (process.env.NODE_ENV === 'development' && !warned) {
warned = true
console.warn(`[table-worker] ${message}`)
}
}
const memoized = tableMemo({
table,
fnName: `table.get${capitalize(stage)}RowModel`,
// The per-stage version bumps only when this stage's payload actually
// changed, so stages the worker reported as `unchanged` skip the O(n)
// rebuild entirely (the re-render itself rides the state slice bump).
memoDeps: () => [
table.getCoreRowModel(),
getTableWorkerBridge(tableWorker, table).stageVersions[stage],
],
fn: () => {
const bridge = getTableWorkerBridge(tableWorker, table)
const payload = bridge.results[stage]
if (!payload) {
if (bridge.resultRequestId > 0) {View on GitHub (pinned to d01c01bedb)
Solutions
- Register the missing factory in the worker's `initTableWorker()` config (e.g. add sortedRowModel/filteredRowModel for every offloaded stage).
- Offload the full contiguous prefix: for every upstream stage in filtered -> grouped -> sorted -> expanded that the table uses, also call `createWorkerRowModel(tableWorker, '<upstream>')`.
- Rebuild/deploy the worker bundle so it matches the main-thread stage configuration.
- Ensure the worker file actually initializes the bridge (initTableWorker) before the table reads row models.
Example fix
// before (main thread)
features: tableFeatures({
workerRowModelsFeature,
sortedRowModel: createWorkerRowModel(tableWorker, 'sorted'),
})
// worker entry: initTableWorker({ createWorker }) // no factories -> no result
// after (worker entry)
initTableWorker({
createWorker,
filteredRowModel: createWorkerFilteredRowModel(),
groupedRowModel: createWorkerGroupedRowModel(),
sortedRowModel: createWorkerSortedRowModel(),
})
// main thread offloads the same contiguous prefix:
// filteredRowModel: createWorkerRowModel(tableWorker, 'filtered'),
// groupedRowModel: createWorkerRowModel(tableWorker, 'grouped'),
// sortedRowModel: createWorkerRowModel(tableWorker, 'sorted'), Defensive patterns
Strategy: validation
Validate before calling
// Main thread: assert every offloaded stage is registered in the worker config
const offloaded = ['filtered', 'grouped', 'sorted'] // stages passed to createWorkerRowModel
const workerStages = ['filtered', 'grouped', 'sorted'] // stages configured in initTableWorker()
const missing = offloaded.filter((s) => !workerStages.includes(s))
if (missing.length) throw new Error(`Worker missing factories for: ${missing.join(', ')}`)
// Contiguity: offloaded stages must be a prefix of the pipeline
const pipeline = ['filtered', 'grouped', 'sorted', 'expanded']
const prefix = pipeline.slice(0, offloaded.length)
if (prefix.some((s) => !offloaded.includes(s))) {
throw new Error('Offloaded stages must form a contiguous prefix of the pipeline')
} Type guard
function isContiguousWorkerOffload(stages) {
const pipeline = ['filtered', 'grouped', 'sorted', 'expanded']
const idx = stages.map((s) => pipeline.indexOf(s)).sort((a, b) => a - b)
return idx.every((v, i) => v === i)
} Try / catch
// Worker round trips are async; a failed/absent result silently degrades to the
// pre-stage model, so guard reads if you must have worker output:
try {
const result = await tableWorker.request({ type: 'run', requestId })
if (!result?.results?.sorted) {
throw new Error(`[table-worker] no 'sorted' result — check initTableWorker() config`)
}
} catch (err) {
console.error('[table-worker] stage failed, main thread fallback in effect:', err)
} Prevention
- Keep a single source of truth listing offloaded stages and generate both the app-side createWorkerRowModel calls and the worker-side initTableWorker config from it.
- Always offload the contiguous prefix (filtered -> grouped -> sorted -> expanded), never a downstream stage alone.
- Rebuild the worker bundle in CI whenever stage configuration changes.
- Watch for the '[table-worker]' dev warning in tests — it means results are silently stale.
When it happens
Trigger: Using `createWorkerRowModel(tableWorker, 'sorted')` (or 'grouped'/'expanded') while the worker bundle's `initTableWorker()` does not register the matching factory; or offloading a downstream stage without offloading the required contiguous upstream prefix (e.g. worker 'sorted' but main-thread 'filtered'/'grouped'). The warning only fires after a real round trip (resultRequestId > 0) and only once per stage.
Common situations: Registering `createWorkerRowModel` in the app but forgetting to add the same stage to the worker entry's `initTableWorker()` config; partial offloads that violate the contiguous-prefix rule; stale worker bundle predating newly added stages; results appearing stale because the stage silently returns its pre-stage model.
Related errors
- globalFilterFn '${String(globalFilterFn)}' is not registered
- ${message}
- aggregationFn '${String(ref)}' for column '${column.id}' is
- aggregationFn at index ${i} for column '${column.id}' needs
- aggregationFn id '${id}' for column '${column.id}' is duplic
AI-assisted analysis of TanStack/table@d01c01bedb (2026-08-28).
Data as JSON: /api/errors/368cd256170c0346.
Report an issue: GitHub.