Skip to content

Commit 12c237d

Browse files
committed
fix: CLI tools page hangs — add timeout to runtime status checks
Server-side: wrap getCliRuntimeStatus() in 5s Promise.race timeout Client-side: add 8s AbortController timeout to fetchToolStatuses() Prevents entire page from staying in skeleton state forever when binary checks hang on VPS
1 parent aeb3632 commit 12c237d

2 files changed

Lines changed: 21 additions & 5 deletions

File tree

src/app/(dashboard)/dashboard/cli-tools/CLIToolsPageClient.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,13 +66,17 @@ export default function CLIToolsPageClient({ machineId }) {
6666

6767
const fetchToolStatuses = async () => {
6868
try {
69-
const res = await fetch("/api/cli-tools/status");
69+
const controller = new AbortController();
70+
const timeoutId = setTimeout(() => controller.abort(), 8000); // 8s client timeout
71+
const res = await fetch("/api/cli-tools/status", { signal: controller.signal });
72+
clearTimeout(timeoutId);
7073
if (res.ok) {
7174
const data = await res.json();
7275
setToolStatuses(data || {});
7376
}
7477
} catch (error) {
75-
console.log("Error fetching CLI tool statuses:", error);
78+
// Timeout or network error — proceed without statuses
79+
console.log("CLI tool status check timed out or failed:", error);
7680
} finally {
7781
setStatusesLoaded(true);
7882
}

src/app/api/cli-tools/status/route.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,23 @@ export async function GET() {
5353
try {
5454
const statuses = {};
5555

56-
// Run all runtime checks in parallel
56+
// Run all runtime checks in parallel with individual timeouts
57+
const RUNTIME_CHECK_TIMEOUT = 5000; // 5s per tool max
5758
await Promise.all(
5859
CLI_TOOL_IDS.map(async (toolId) => {
5960
try {
60-
const runtime = await getCliRuntimeStatus(toolId);
61+
const runtime = (await Promise.race([
62+
getCliRuntimeStatus(toolId),
63+
new Promise((_, reject) =>
64+
setTimeout(() => reject(new Error("Timeout")), RUNTIME_CHECK_TIMEOUT)
65+
),
66+
])) as {
67+
installed: boolean;
68+
runnable: boolean;
69+
command?: string;
70+
commandPath?: string;
71+
reason?: string;
72+
};
6173
statuses[toolId] = {
6274
installed: runtime.installed,
6375
runnable: runtime.runnable,
@@ -69,7 +81,7 @@ export async function GET() {
6981
statuses[toolId] = {
7082
installed: false,
7183
runnable: false,
72-
reason: error.message,
84+
reason: error.message || "Check failed",
7385
};
7486
}
7587
})

0 commit comments

Comments
 (0)