Fix crash when row arrays contain undefined entries (TenantId TypeError)#250
Open
andrescodas wants to merge 1 commit intomicrosoft:mainfrom
Open
Fix crash when row arrays contain undefined entries (TenantId TypeError)#250andrescodas wants to merge 1 commit intomicrosoft:mainfrom
andrescodas wants to merge 1 commit intomicrosoft:mainfrom
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
This PR hardens table ingestion and rendering paths against sparse row arrays by filtering out undefined/null entries before property access, preventing crashes like TypeError: Cannot read properties of undefined.
Changes:
- Add defensive row sanitization (
(rows || []).filter(Boolean)/.find(Boolean)) across reducers, thunks, and multiple table/visualization renderers. - Replace direct first-row indexing (
rows[0]) with safe first-row selection to avoid empty/sparse-array failures. - Add null-guard in virtualized grid row rendering (
itemContent) to prevent rendering exceptions.
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| src/views/VisualizationView.tsx | Filters sparse rows for table charts and availability checks; filters sampled rows returned from server. |
| src/views/SelectableDataGrid.tsx | Ensures displayed rows are sanitized and adds a null-guard in the virtualized row renderer. |
| src/views/RefreshDataDialog.tsx | Validates columns using the first non-null row instead of newRows[0]. |
| src/views/ReactTable.tsx | Filters out nullish rows before rendering paged table body rows. |
| src/views/EncodingBox.tsx | Filters nullish rows before extracting domain values for encodings. |
| src/views/DataView.tsx | Filters nullish rows before rendering and before sampling for column width calculation. |
| src/views/DBTableManager.tsx | Filters sample_rows from DB metadata before building preview/load tables. |
| src/components/ComponentType.tsx | Sanitizes rows in createDictTable() and uses sanitized rows for type inference. |
| src/app/tableThunks.ts | Filters nullish rows from external-source fetch responses before creating tables. |
| src/app/dfSlice.tsx | Adds safeNewRows for inference/hash computation when updating table rows. |
Comments suppressed due to low confidence (1)
src/views/VisualizationView.tsx:675
setVisTableRows((data.rows || []).filter(Boolean))filters the server response, but the cache stores the unfiltereddata.rows. When the cache is hit later,setVisTableRows(cached.rows)will reintroduceundefined/nullrows and can bring back the same rendering crash. Cache the filtered rows (and/or filter again when reading fromdisplayRowsCache).
if (data.status == "success") {
setVisTableRows((data.rows || []).filter(Boolean));
setVisTableTotalRowCount(data.total_row_count);
setDataVersion(versionId);
// Cache for instant reuse on chart revisit
displayRowsCache.set(versionId, { rows: data.rows, totalCount: data.total_row_count });
} else {
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+658
to
673
| const safeNewRows = newRows.filter(Boolean); | ||
| for (let name of t.names) { | ||
| if (newRows.length > 0 && name in newRows[0]) { | ||
| if (safeNewRows.length > 0 && name in safeNewRows[0]) { | ||
| newMetadata[name] = { | ||
| ...newMetadata[name], | ||
| type: inferTypeFromValueArray(newRows.map(r => r[name])), | ||
| type: inferTypeFromValueArray(safeNewRows.map(r => r[name])), | ||
| }; | ||
| } | ||
| } | ||
| // Update lastRefreshed timestamp if source exists | ||
| const updatedSource = t.source ? { ...t.source, lastRefreshed: Date.now() } : undefined; | ||
| // Use provided content hash (from backend for virtual/DB tables) or compute locally | ||
| // For virtual tables, backend hash reflects full table; for stream tables, compute from actual rows | ||
| const newContentHash = providedContentHash || computeContentHash(newRows, t.names); | ||
| const newContentHash = providedContentHash || computeContentHash(safeNewRows, t.names); | ||
| return { ...t, rows: newRows, metadata: newMetadata, source: updatedSource, contentHash: newContentHash }; | ||
| } |
| const updatedSource = t.source ? { ...t.source, lastRefreshed: Date.now() } : undefined; | ||
| const newContentHash = providedContentHash || computeContentHash(newRows, t.names); | ||
| const newContentHash = providedContentHash || computeContentHash(safeNewRows, t.names); | ||
| return { ...t, rows: newRows, metadata: newMetadata, source: updatedSource, contentHash: newContentHash }; |
Comment on lines
88
to
92
| {rows.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage) | ||
| .filter(Boolean) | ||
| .map((row, i) => { | ||
| return ( | ||
| <TableRow hover tabIndex={-1} key={i} sx={{ background: i % 2 == 0 ? '#F0F0F0' : "none" }}> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The application crashes with
TypeError: Cannot read properties of undefined (reading 'TenantId')when rendering tables whose row arrays containundefinedornullentries.TenantIdis a Kusto data column name — the real issue is that.map()iterates over row objects and one or more elements in the array areundefined, causing any property access to throw.This affects users loading data from external sources (e.g., Kusto/database connectors) where the server response may include sparse or null row entries.
Fix
Added
.filter(Boolean)guards to filter outundefined/nullrow entries before they are iterated or indexed. Applied defensively at both data ingestion points (API responses, reducers) and rendering paths:Data ingestion / state management:
createDictTable(): filter rows before deriving column names and type inferenceupdateTableRows,updateMultipleTableRows,addDerivedField,removeDerivedFieldreducersloadExternalSourceAPI response handlingRendering:
SAMPLE_TABLEAPI response, anditemContentnull guardrenderTableChart,createVisTableRowsLocal,checkChartAvailability,checkChartAvailabilityOnPreparedData, and display row fetch handlersample_rowsusage sites.find(Boolean)instead of[0]