clawwork-ai/ClawWork

[Bug] SkillsSection toggle / install buttons stay spinning when IPC throws

Open

#399 opened on Apr 15, 2026

View on GitHub
 (1 comment) (0 reactions) (0 assignees)TypeScript (63 forks)github user discovery
area/uigood first issuekind/bug

Repository metrics

Stars
 (519 stars)
PR merge metrics
 (PR metrics pending)

Description

Problem

SkillsSection.handleToggleEnabled and ClawHubTab.handleInstall both use the pattern: setXxx((prev) => new Set(prev).add(key))await window.clawwork.xxx(...)setXxx((prev) => remove key).

The cleanup setXxx sits after the await, outside any try/finally. If the IPC call throws, the skill / slug is never removed from the "toggling" / "installing" set and its Power button / Install button stays spinning forever. The user has no way to retry without refreshing the entire section.

Location

File: packages/desktop/src/renderer/layouts/Settings/sections/SkillsSection.tsx:710-732 (toggle) and 566-583 (install)

// handleToggleEnabled
setTogglingKeys((prev) => new Set(prev).add(skill.skillKey));
const newEnabled = skill.disabled;
const res = await window.clawwork.updateSkill(...);  // throw → stuck
if (res.ok) { ... } else { ... }
setTogglingKeys((prev) => { const next = new Set(prev); next.delete(skill.skillKey); return next; });

// handleInstall
setInstallingSlugs((prev) => new Set(prev).add(slug));
const res = await window.clawwork.installSkill(gatewayId, { source: 'clawhub', slug });  // throw → stuck
// ... branches ...
setInstallingSlugs((prev) => { const next = new Set(prev); next.delete(slug); return next; });

Fix Approach

Wrap the IPC call in try/finally so the set is always cleaned up:

setTogglingKeys((prev) => new Set(prev).add(skill.skillKey));
try {
  const res = await window.clawwork.updateSkill(...);
  if (res.ok) { ... } else { toast.error(...); }
} catch (err) {
  console.error('[SkillsSection] toggle failed:', err);
  toast.error(t('settings.skillUpdateFailed'));
} finally {
  setTogglingKeys((prev) => {
    const next = new Set(prev);
    next.delete(skill.skillKey);
    return next;
  });
}

Apply the same pattern to handleInstall.

Verification

  1. Run pnpm check — must pass.
  2. Manual: simulate IPC throw during a skill toggle — the button must return to its steady state and a toast should surface the error.

Context

  • WG: UI & Design System
  • Priority: Low (good first issue)
  • Estimated effort: 15-20 minutes

Contributor guide