From 4d7a13e803ec576634e4ba649ff40e4be6569f27 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 18 Nov 2025 03:12:36 +0000 Subject: [PATCH 1/2] fix(ui): handle undefined totalCapacity in Nodes admin page Add optional chaining and nullish coalescing to safely access stats.totalCapacity.cpu and stats.totalCapacity.memory properties. When totalCapacity is undefined, displays 'N/A' instead of crashing. --- ui/src/pages/admin/Nodes.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/src/pages/admin/Nodes.tsx b/ui/src/pages/admin/Nodes.tsx index 4dff4473..3b8dac3b 100644 --- a/ui/src/pages/admin/Nodes.tsx +++ b/ui/src/pages/admin/Nodes.tsx @@ -494,7 +494,7 @@ export default function AdminNodes() { Total CPU - {stats.totalCapacity.cpu} + {stats.totalCapacity?.cpu ?? 'N/A'} {stats.totalUsage && ( @@ -519,7 +519,7 @@ export default function AdminNodes() { Total Memory - {stats.totalCapacity.memory} + {stats.totalCapacity?.memory ?? 'N/A'} {stats.totalUsage && ( From 0b437564491dfcb549584cfbbe5caced3dfecbe2 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 18 Nov 2025 03:18:06 +0000 Subject: [PATCH 2/2] fix(ui): add defensive null checks in Nodes admin page - Add optional chaining for totalCapacity.cpu and totalCapacity.memory - Add null checks for node properties (allocatable, info, labels, taints) - Add empty state message when no nodes are found - Show helpful troubleshooting hints for cluster connectivity issues Prevents TypeErrors when API returns incomplete or undefined node data. --- ui/src/pages/admin/Nodes.tsx | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/ui/src/pages/admin/Nodes.tsx b/ui/src/pages/admin/Nodes.tsx index 3b8dac3b..8d5674bf 100644 --- a/ui/src/pages/admin/Nodes.tsx +++ b/ui/src/pages/admin/Nodes.tsx @@ -397,7 +397,7 @@ export default function AdminNodes() { }; const isNodeSchedulable = (node: NodeInfo) => { - return !node.taints.some(t => t.effect === 'NoSchedule' || t.effect === 'NoExecute'); + return !node.taints?.some(t => t.effect === 'NoSchedule' || t.effect === 'NoExecute'); }; if (loading) { @@ -537,6 +537,18 @@ export default function AdminNodes() { )} + {nodes.length === 0 && !loading && ( + + No nodes found. This could mean: +
    +
  • The Kubernetes cluster is not accessible
  • +
  • The API server cannot connect to the cluster
  • +
  • No nodes have been registered yet
  • +
+ Check that your kubeconfig is properly configured and the cluster is running. +
+ )} + {nodes.map((node) => ( @@ -551,7 +563,7 @@ export default function AdminNodes() {
- {node.info.osImage} • {node.info.kubeletVersion} + {node.info?.osImage ?? 'Unknown OS'} • {node.info?.kubeletVersion ?? 'Unknown version'} {node.provider && ( @@ -579,7 +591,7 @@ export default function AdminNodes() { CPU - {node.allocatable.cpu} cores + {node.allocatable?.cpu ?? 'N/A'} cores {node.usage && ( @@ -592,7 +604,7 @@ export default function AdminNodes() { Memory - {node.allocatable.memory} + {node.allocatable?.memory ?? 'N/A'} {node.usage && ( @@ -605,10 +617,10 @@ export default function AdminNodes() { Pods - {node.pods} / {node.allocatable.pods} + {node.pods ?? 0} / {node.allocatable?.pods ?? 'N/A'} - {node.allocatable['nvidia.com/gpu'] && ( + {node.allocatable?.['nvidia.com/gpu'] && ( GPUs @@ -629,7 +641,7 @@ export default function AdminNodes() { setLabelDialogOpen(true); }} > - Labels ({Object.keys(node.labels).length}) + Labels ({Object.keys(node.labels ?? {}).length}) @@ -690,7 +702,7 @@ export default function AdminNodes() { Current Labels - {selectedNode && Object.entries(selectedNode.labels).map(([key, value]) => ( + {selectedNode && Object.entries(selectedNode.labels ?? {}).map(([key, value]) => ( - {selectedNode?.taints.map((taint) => ( + {selectedNode?.taints?.map((taint) => (