{"record":{"id":"368cd256170c0346","repo":"TanStack/table","slug":"table-worker-message","errorCode":null,"errorMessage":"[table-worker] ${message}","messagePattern":"\\[table-worker\\] (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"packages/table-core/src/worker/createWorkerRowModel.ts","lineNumber":65,"sourceCode":" */\nexport function createWorkerRowModel(\n  tableWorker: TableWorker,\n  stage: TableWorkerStage,\n) {\n  return <TFeatures extends TableFeatures, TData extends RowData>(\n    _table: Table<TFeatures, TData>,\n  ): (() => RowModel<TFeatures, TData>) => {\n    const table = _table as unknown as AnyTable\n    // Stages register per table as their factories initialize (first read).\n    // The first render may therefore fire one extra coalesced request while\n    // the stage set grows; single-flight makes it a cheap trailing request.\n    getTableWorkerBridge(tableWorker, table).stages.add(stage)\n    let warned = false\n\n    const warnOnce = (message: string) => {\n      if (process.env.NODE_ENV === 'development' && !warned) {\n        warned = true\n        console.warn(`[table-worker] ${message}`)\n      }\n    }\n\n    const memoized = tableMemo({\n      table,\n      fnName: `table.get${capitalize(stage)}RowModel`,\n      // The per-stage version bumps only when this stage's payload actually\n      // changed, so stages the worker reported as `unchanged` skip the O(n)\n      // rebuild entirely (the re-render itself rides the state slice bump).\n      memoDeps: () => [\n        table.getCoreRowModel(),\n        getTableWorkerBridge(tableWorker, table).stageVersions[stage],\n      ],\n      fn: () => {\n        const bridge = getTableWorkerBridge(tableWorker, table)\n        const payload = bridge.results[stage]\n        if (!payload) {\n          if (bridge.resultRequestId > 0) {","sourceCodeStart":47,"sourceCodeEnd":83,"githubUrl":"https://github.com/TanStack/table/blob/d01c01bedbab0ff6c2641f18b2fc9a11545d9bf6/packages/table-core/src/worker/createWorkerRowModel.ts#L47-L83","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before (main thread)\nfeatures: tableFeatures({\n  workerRowModelsFeature,\n  sortedRowModel: createWorkerRowModel(tableWorker, 'sorted'),\n})\n// worker entry: initTableWorker({ createWorker }) // no factories -> no result\n\n// after (worker entry)\ninitTableWorker({\n  createWorker,\n  filteredRowModel: createWorkerFilteredRowModel(),\n  groupedRowModel: createWorkerGroupedRowModel(),\n  sortedRowModel: createWorkerSortedRowModel(),\n})\n// main thread offloads the same contiguous prefix:\n// filteredRowModel: createWorkerRowModel(tableWorker, 'filtered'),\n// groupedRowModel: createWorkerRowModel(tableWorker, 'grouped'),\n// sortedRowModel: createWorkerRowModel(tableWorker, 'sorted'),","handlingStrategy":"validation","validationCode":"// Main thread: assert every offloaded stage is registered in the worker config\nconst offloaded = ['filtered', 'grouped', 'sorted'] // stages passed to createWorkerRowModel\nconst workerStages = ['filtered', 'grouped', 'sorted'] // stages configured in initTableWorker()\nconst missing = offloaded.filter((s) => !workerStages.includes(s))\nif (missing.length) throw new Error(`Worker missing factories for: ${missing.join(', ')}`)\n// Contiguity: offloaded stages must be a prefix of the pipeline\nconst pipeline = ['filtered', 'grouped', 'sorted', 'expanded']\nconst prefix = pipeline.slice(0, offloaded.length)\nif (prefix.some((s) => !offloaded.includes(s))) {\n  throw new Error('Offloaded stages must form a contiguous prefix of the pipeline')\n}","typeGuard":"function isContiguousWorkerOffload(stages) {\n  const pipeline = ['filtered', 'grouped', 'sorted', 'expanded']\n  const idx = stages.map((s) => pipeline.indexOf(s)).sort((a, b) => a - b)\n  return idx.every((v, i) => v === i)\n}","tryCatchPattern":"// Worker round trips are async; a failed/absent result silently degrades to the\n// pre-stage model, so guard reads if you must have worker output:\ntry {\n  const result = await tableWorker.request({ type: 'run', requestId })\n  if (!result?.results?.sorted) {\n    throw new Error(`[table-worker] no 'sorted' result — check initTableWorker() config`)\n  }\n} catch (err) {\n  console.error('[table-worker] stage failed, main thread fallback in effect:', err)\n}","preventionTips":["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."],"tags":["web-worker","configuration","stale-data","dev-warning"],"backgroundTag":"worker-stage-misconfiguration","analyzedSha":"d01c01bedbab0ff6c2641f18b2fc9a11545d9bf6","analyzedAt":"2026-08-28T21:52:44.679Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}