From e545fa219499f28a3e45a9a5b5fdad6bb3d8d70d Mon Sep 17 00:00:00 2001
From: Felix <24791380+vcfgv@users.noreply.github.com>
Date: Thu, 30 Apr 2026 18:33:03 +0800
Subject: [PATCH] fix: improve error handling by returning specific error
messages in skill routes and IPC handlers (#945)
---
electron/api/routes/skills.ts | 18 +++++++++---------
electron/main/ipc-handlers.ts | 8 ++++----
src/pages/Skills/index.tsx | 2 +-
src/stores/skills.ts | 13 ++++++++-----
4 files changed, 22 insertions(+), 19 deletions(-)
diff --git a/electron/api/routes/skills.ts b/electron/api/routes/skills.ts
index 21f5c6c..49559d1 100644
--- a/electron/api/routes/skills.ts
+++ b/electron/api/routes/skills.ts
@@ -28,7 +28,7 @@ export async function handleSkillRoutes(
env: body.env,
}));
} catch (error) {
- sendJson(res, 500, { success: false, error: String(error) });
+ sendJson(res, 500, { success: false, error: error instanceof Error ? error.message : String(error) });
}
return true;
}
@@ -58,7 +58,7 @@ export async function handleSkillRoutes(
skills: filterEnabledQuickAccessSkills(scannedSkills, runtimeSkills, configs),
});
} catch (error) {
- sendJson(res, 500, { success: false, error: String(error) });
+ sendJson(res, 500, { success: false, error: error instanceof Error ? error.message : String(error) });
}
return true;
}
@@ -70,7 +70,7 @@ export async function handleSkillRoutes(
capability: await ctx.clawHubService.getMarketplaceCapability(),
});
} catch (error) {
- sendJson(res, 500, { success: false, error: String(error) });
+ sendJson(res, 500, { success: false, error: error instanceof Error ? error.message : String(error) });
}
return true;
}
@@ -83,7 +83,7 @@ export async function handleSkillRoutes(
results: await ctx.clawHubService.search(body),
});
} catch (error) {
- sendJson(res, 500, { success: false, error: String(error) });
+ sendJson(res, 500, { success: false, error: error instanceof Error ? error.message : String(error) });
}
return true;
}
@@ -94,7 +94,7 @@ export async function handleSkillRoutes(
await ctx.clawHubService.install(body);
sendJson(res, 200, { success: true });
} catch (error) {
- sendJson(res, 500, { success: false, error: String(error) });
+ sendJson(res, 500, { success: false, error: error instanceof Error ? error.message : String(error) });
}
return true;
}
@@ -105,7 +105,7 @@ export async function handleSkillRoutes(
await ctx.clawHubService.uninstall(body);
sendJson(res, 200, { success: true });
} catch (error) {
- sendJson(res, 500, { success: false, error: String(error) });
+ sendJson(res, 500, { success: false, error: error instanceof Error ? error.message : String(error) });
}
return true;
}
@@ -114,7 +114,7 @@ export async function handleSkillRoutes(
try {
sendJson(res, 200, { success: true, results: await ctx.clawHubService.listInstalled() });
} catch (error) {
- sendJson(res, 500, { success: false, error: String(error) });
+ sendJson(res, 500, { success: false, error: error instanceof Error ? error.message : String(error) });
}
return true;
}
@@ -125,7 +125,7 @@ export async function handleSkillRoutes(
await ctx.clawHubService.openSkillReadme(body.skillKey || body.slug || '', body.slug, body.baseDir);
sendJson(res, 200, { success: true });
} catch (error) {
- sendJson(res, 500, { success: false, error: String(error) });
+ sendJson(res, 500, { success: false, error: error instanceof Error ? error.message : String(error) });
}
return true;
}
@@ -136,7 +136,7 @@ export async function handleSkillRoutes(
await ctx.clawHubService.openSkillPath(body.skillKey || body.slug || '', body.slug, body.baseDir);
sendJson(res, 200, { success: true });
} catch (error) {
- sendJson(res, 500, { success: false, error: String(error) });
+ sendJson(res, 500, { success: false, error: error instanceof Error ? error.message : String(error) });
}
return true;
}
diff --git a/electron/main/ipc-handlers.ts b/electron/main/ipc-handlers.ts
index c1909e3..d50c9b1 100644
--- a/electron/main/ipc-handlers.ts
+++ b/electron/main/ipc-handlers.ts
@@ -2085,7 +2085,7 @@ function registerClawHubHandlers(clawHubService: ClawHubService): void {
const results = await clawHubService.search(params);
return { success: true, results };
} catch (error) {
- return { success: false, error: String(error) };
+ return { success: false, error: error instanceof Error ? error.message : String(error) };
}
});
@@ -2095,7 +2095,7 @@ function registerClawHubHandlers(clawHubService: ClawHubService): void {
await clawHubService.install(params);
return { success: true };
} catch (error) {
- return { success: false, error: String(error) };
+ return { success: false, error: error instanceof Error ? error.message : String(error) };
}
});
@@ -2105,7 +2105,7 @@ function registerClawHubHandlers(clawHubService: ClawHubService): void {
await clawHubService.uninstall(params);
return { success: true };
} catch (error) {
- return { success: false, error: String(error) };
+ return { success: false, error: error instanceof Error ? error.message : String(error) };
}
});
@@ -2115,7 +2115,7 @@ function registerClawHubHandlers(clawHubService: ClawHubService): void {
const results = await clawHubService.listInstalled();
return { success: true, results };
} catch (error) {
- return { success: false, error: String(error) };
+ return { success: false, error: error instanceof Error ? error.message : String(error) };
}
});
diff --git a/src/pages/Skills/index.tsx b/src/pages/Skills/index.tsx
index 621a79b..6072ab2 100644
--- a/src/pages/Skills/index.tsx
+++ b/src/pages/Skills/index.tsx
@@ -685,7 +685,7 @@ export function Skills() {
{SEARCH_ERROR_CODES.has(searchError.replace('Error: ', ''))
? t(`toast.${searchError.replace('Error: ', '')}`, { path: skillsDirPath })
- : t('marketplace.searchError')}
+ : searchError}
)}
diff --git a/src/stores/skills.ts b/src/stores/skills.ts
index aeac44f..a12edca 100644
--- a/src/stores/skills.ts
+++ b/src/stores/skills.ts
@@ -39,7 +39,7 @@ type ClawHubListResult = {
function mapErrorCodeToSkillErrorKey(
code: AppError['code'],
operation: 'fetch' | 'search' | 'install',
-): string {
+): string | null {
if (code === 'TIMEOUT') {
return operation === 'search'
? 'searchTimeoutError'
@@ -54,7 +54,7 @@ function mapErrorCodeToSkillErrorKey(
? 'installRateLimitError'
: 'fetchRateLimitError';
}
- return 'rateLimitError';
+ return null;
}
interface SkillsState {
@@ -172,8 +172,9 @@ export const useSkillsStore = create((set, get) => ({
} catch (error) {
console.error('Failed to fetch skills:', error);
const appError = normalizeAppError(error, { module: 'skills', operation: 'fetch' });
+ const errorKey = mapErrorCodeToSkillErrorKey(appError.code, 'fetch');
// Preserve previous skills on error (stale-while-revalidate).
- set((prev) => ({ loading: false, error: mapErrorCodeToSkillErrorKey(appError.code, 'fetch'), skills: prev.skills }));
+ set((prev) => ({ loading: false, error: errorKey ?? appError.message, skills: prev.skills }));
}
},
@@ -194,7 +195,8 @@ export const useSkillsStore = create((set, get) => ({
}
} catch (error) {
const appError = normalizeAppError(error, { module: 'skills', operation: 'search' });
- set({ searchError: mapErrorCodeToSkillErrorKey(appError.code, 'search') });
+ const errorKey = mapErrorCodeToSkillErrorKey(appError.code, 'search');
+ set({ searchError: errorKey ?? appError.message });
} finally {
set({ searching: false });
}
@@ -212,7 +214,8 @@ export const useSkillsStore = create((set, get) => ({
module: 'skills',
operation: 'install',
});
- throw new Error(mapErrorCodeToSkillErrorKey(appError.code, 'install'));
+ const errorKey = mapErrorCodeToSkillErrorKey(appError.code, 'install');
+ throw new Error(errorKey ?? appError.message);
}
// Refresh skills after install
await get().fetchSkills();