Date: December 24, 2025 Status: ✅ FIXED
Error:
Cannot read properties of undefined (reading 'toFixed')
at ExplainPlanTab.js:281
Root Cause:
- Backend model uses
estimatedCost(without "Ms" suffix) - Frontend was trying to access
analysis.estimatedCostMs - Field was undefined, causing
.toFixed(2)to fail
Fix:
- Changed
analysis.estimatedCostMstoanalysis.estimatedCost - Added proper null/undefined checks
- File:
src/components/tabs/ExplainPlanTab.js:278-282
Before:
{analysis.estimatedCostMs !== null && (
<span className={styles.metricValue}>{analysis.estimatedCostMs.toFixed(2)}ms</span>
)}After:
{analysis.estimatedCost !== null && analysis.estimatedCost !== undefined && (
<span className={styles.metricValue}>{analysis.estimatedCost.toFixed(2)}</span>
)}Root Cause:
- Backend model returns
planTree(single ExplainPlanNode object) - Frontend was expecting
planNodes(array)
Fix:
- Changed
analysis.planNodestoanalysis.planTree - Updated rendering logic to handle single node instead of array
- File:
src/components/tabs/ExplainPlanTab.js:355-363
Before:
{analysis.planNodes && analysis.planNodes.length > 0 ? (
analysis.planNodes.map((node, idx) => renderPlanNode(node, idx))
) : (
<div>No execution plan nodes available</div>
)}After:
{analysis.planTree ? (
renderPlanNode(analysis.planTree, 0)
) : (
<div>No execution plan available</div>
)}Root Cause:
- JavaScript treats
null !== undefinedas true - Checks like
value !== nulldon't catch undefined values - Caused runtime errors when calling methods on undefined
Fixes Applied:
-
Line 272-276:
estimatedRowscheck// Added undefined check {analysis.estimatedRows !== null && analysis.estimatedRows !== undefined && ( <span>{analysis.estimatedRows.toLocaleString()}</span> )}
-
Line 278-282:
estimatedCostcheck (also fixed field name){analysis.estimatedCost !== null && analysis.estimatedCost !== undefined && ( <span>{analysis.estimatedCost.toFixed(2)}</span> )}
-
Line 320-324:
affectedRowscheck{issue.affectedRows !== null && issue.affectedRows !== undefined && issue.affectedRows > 0 && ( <div>Affected Rows: {issue.affectedRows.toLocaleString()}</div> )}
- Line 438-440:
efficiencyRatiocheck// Added ternary to handle undefined {query.efficiencyRatio !== null && query.efficiencyRatio !== undefined ? `${(query.efficiencyRatio * 100).toFixed(4)}%` : 'N/A'}
The actual backend fields (from ExplainPlanAnalysis.java):
// What the backend ACTUALLY returns:
private Double estimatedCost; // NOT estimatedCostMs
private Double actualCost;
private Double estimatedRows;
private Long actualRows;
private ExplainPlanNode planTree; // NOT planNodes (single node, not array)- Navigate to
http://localhost:3000 - Select a database connection
- Click the ⚡ EXPLAIN Plan tab
- Enter this query:
SELECT * FROM users WHERE id = 1;
- Click "Analyze Query"
Expected Results:
- ✅ No runtime errors
- ✅ Performance score displays correctly
- ✅ Estimated Cost shows (if available) or is hidden
- ✅ Estimated Rows shows (if available) or is hidden
- ✅ Execution plan tree displays
- ✅ Issues display with severity badges
- ✅ AI summary appears
- Check the "Use EXPLAIN ANALYZE" checkbox
- Click "Analyze Query"
Expected Results:
- ✅ Query executes successfully
- ✅ Additional metrics appear (actual rows, actual cost)
- ✅ No errors with undefined fields
For all optional numeric fields that need method calls (.toFixed(), .toLocaleString()):
// Pattern 1: Conditional rendering
{value !== null && value !== undefined && (
<span>{value.toFixed(2)}</span>
)}
// Pattern 2: Ternary with fallback
{value !== null && value !== undefined
? `${value.toFixed(2)}%`
: 'N/A'}
// Pattern 3: Optional chaining (cleaner, modern)
{value?.toLocaleString()} // Safe, returns undefined if value is null/undefinedIn JavaScript:
null !== undefined // true ⚠️
null !== null // false ✅
undefined !== undefined // false ✅
// So checking only !== null is NOT enough:
let value = undefined
if (value !== null) {
value.toFixed(2) // ❌ ERROR: Cannot read property 'toFixed' of undefined
}
// Need to check both:
if (value !== null && value !== undefined) {
value.toFixed(2) // ✅ Safe
}-
✅
src/components/tabs/ExplainPlanTab.js- Line 56-58: Fixed planTree auto-expand
- Line 272-282: Fixed estimatedRows and estimatedCost checks
- Line 320-324: Fixed affectedRows check
- Line 355-363: Fixed planTree rendering
-
✅
src/components/tabs/SlowQueryAnalysisTab.js- Line 438-440: Fixed efficiencyRatio check
- Backend:
estimatedCost→ Frontend:estimatedCost✅ - Backend:
planTree→ Frontend:planTree✅ - Keep naming consistent to avoid confusion
- Backend returns single node (
planTree), not array (planNodes) - Check model files before writing frontend code
- Consider adding TypeScript for type safety
- Always check for both
nullandundefined - Use optional chaining (
?.) where possible - Provide fallback values (e.g., 'N/A')
- API might return partial data
- Fields can be null/undefined even if documented
- Always handle missing fields gracefully
-
Add TypeScript (Future)
- Define interfaces matching backend models
- Catch field name mismatches at compile time
- Get autocomplete for API responses
-
Add Loading Skeletons
- Show placeholder content while loading
- Better UX than blank screen
- Prevents layout shift
-
Add Error Boundary
- Catch unexpected runtime errors
- Show friendly error message
- Prevent entire app crash
-
API Response Validation
- Validate API response structure
- Log warnings for missing expected fields
- Easier debugging in production
interface ExplainPlanAnalysis {
connectionId: string
query: string
planTree?: ExplainPlanNode // Single node, optional
issues: PerformanceIssue[]
estimatedCost?: number // Not estimatedCostMs!
estimatedRows?: number
performanceScore: number
aiSummary?: string
// ... other fields
}Total Issues Fixed: 4 Files Modified: 2 Lines Changed: ~15
Status: ✅ All runtime errors resolved Ready for Testing: ✅ YES
The EXPLAIN Plan and Slow Query Analysis tabs should now work correctly without runtime errors. All undefined field access issues have been fixed with proper null/undefined checks and correct field names matching the backend model.
Test Now: Open http://localhost:3000 and try the ⚡ EXPLAIN Plan tab!