{"record":{"id":"651f62f807a9a8f4","repo":"BookStackApp/BookStack","slug":"cell-not-found-at-cords","errorCode":null,"errorMessage":"Cell not found at cords.","messagePattern":"Cell not found at cords\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"resources/js/wysiwyg/lexical/table/LexicalTableNode.ts","lineNumber":259,"sourceCode":"\n    const cell = row[index];\n\n    if (cell == null) {\n      return null;\n    }\n\n    return cell;\n  }\n\n  getDOMCellFromCordsOrThrow(\n    x: number,\n    y: number,\n    table: TableDOMTable,\n  ): TableDOMCell {\n    const cell = this.getDOMCellFromCords(x, y, table);\n\n    if (!cell) {\n      throw new Error('Cell not found at cords.');\n    }\n\n    return cell;\n  }\n\n  getCellNodeFromCords(\n    x: number,\n    y: number,\n    table: TableDOMTable,\n  ): null | TableCellNode {\n    const cell = this.getDOMCellFromCords(x, y, table);\n\n    if (cell == null) {\n      return null;\n    }\n\n    const node = $getNearestNodeFromDOMNode(cell.elem);\n","sourceCodeStart":241,"sourceCodeEnd":277,"githubUrl":"https://github.com/BookStackApp/BookStack/blob/18f8469a1c72f8cc8497e9372635e6dea5028071/resources/js/wysiwyg/lexical/table/LexicalTableNode.ts#L241-L277","documentation":"getDOMCellFromCordsOrThrow resolves grid coordinates to a DOM cell entry via getDOMCellFromCords, which returns null when (x, y) is outside the table or the cell has no DOM representation. This wrapper converts that null into a thrown Error('Cell not found at cords.').","triggerScenarios":"adjustFocusNodeInDirection (keyboard arrow navigation inside a table selection) computing an (x, y) that is out of bounds — e.g. moving past the last row/column — or when the cached TableDOMTable is out of sync with the actual DOM.","commonSituations":"Arrow-key navigation at table edges with stale table measurements; tables modified concurrently (row removed while selection still references old coordinates); observer not yet re-synced after a DOM mutation.","solutions":["Clamp/bounds-check x and y against tableNode.getRows()/getColumns() (or grid dimensions) before direction-based navigation.","Refresh the table DOM data ($getElementForTableNode / getTable) before resolving coordinates after any mutation.","Call the non-throwing getDOMCellFromCords and no-op when it returns null.","Ensure the TableObserver re-reads the DOM after row/column changes so its cached grid matches reality."],"exampleFix":"// before\nconst cell = tableNode.getDOMCellFromCordsOrThrow(x, y, table); // throws off-grid\n\n// after\nconst cell = tableNode.getDOMCellFromCords(x, y, table);\nif (!cell) return; // ignore navigation past table edge","handlingStrategy":"validation","validationCode":"function inBounds(table: TableDOMTable, x: number, y: number): boolean {\n  return y >= 0 && y < table.rows.length && x >= 0 && x < (table.rows[y]?.cells.length ?? 0);\n}","typeGuard":null,"tryCatchPattern":"try {\n  const cell = tableNode.getDOMCellFromCordsOrThrow(x, y, table);\n} catch (e) {\n  if (e instanceof Error && e.message === 'Cell not found at cords.') return; // off-grid navigation\n  throw e;\n}","preventionTips":["Clamp direction-based navigation targets to grid dimensions","Refresh TableDOMTable via getTable after structural mutations","Prefer the nullable getDOMCellFromCords in navigation code"],"tags":["lexical","table","coordinates","keyboard-navigation"],"backgroundTag":"cell-not-found-at-coordinates","analyzedSha":"18f8469a1c72f8cc8497e9372635e6dea5028071","analyzedAt":"2026-09-02T19:49:33.068Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T02:17:09.455Z"}