{"record":{"id":"a0c47a6240260043","repo":"chatboxai/chatbox","slug":"script-not-found-scriptname","errorCode":null,"errorMessage":"Script not found: ${scriptName}","messagePattern":"Script not found: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"src/main/skills/ipc-handlers.ts","lineNumber":188,"sourceCode":"      const { skillName, scriptName, args = [] } = params\n\n      try {\n        if (!skillName || !scriptName) {\n          throw new Error('Skill name and script name are required')\n        }\n\n        if (skillName.includes('..') || skillName.includes('/') || skillName.includes('\\\\')) {\n          throw new Error('Invalid skill name: path traversal not allowed')\n        }\n\n        if (scriptName.includes('..') || scriptName.includes('/') || scriptName.includes('\\\\')) {\n          throw new Error('Invalid script name: path traversal not allowed')\n        }\n\n        const skillsDir = getSkillsDir()\n        const scriptPath = path.join(skillsDir, skillName, 'scripts', scriptName)\n        if (!fs.existsSync(scriptPath)) {\n          throw new Error(`Script not found: ${scriptName}`)\n        }\n        const resolvedSkillsDir = fs.realpathSync(skillsDir)\n        const resolvedScriptPath = fs.realpathSync(scriptPath)\n        if (!resolvedScriptPath.startsWith(`${resolvedSkillsDir}${path.sep}`)) {\n          throw new Error('Script path escapes skills directory')\n        }\n\n        const scriptDir = path.dirname(resolvedScriptPath)\n\n        return await new Promise((resolve) => {\n          const TIMEOUT_MS = 30_000\n          let stdout = ''\n          let stderr = ''\n          let settled = false\n\n          const resolveOnce = (result: {\n            success: boolean\n            stdout: string","sourceCodeStart":170,"sourceCodeEnd":206,"githubUrl":"https://github.com/chatboxai/chatbox/blob/81571269addb6bafb589a920b2883f1e1e084fd1/src/main/skills/ipc-handlers.ts#L170-L206","documentation":"Thrown by the skills:execute-script IPC handler after constructing scriptPath and finding it does not exist via fs.existsSync. This means the skill is installed but the named script is absent — either never installed, deleted, or the SKILL.md manifest referenced a script that was not bundled. The check runs before the realpath containment guard (107), so a missing file short-circuits.","triggerScenarios":"fs.existsSync(path.join(skillsDir, skillName, 'scripts', scriptName)) returns false. The skill directory exists but scripts/{scriptName} is absent. Happens when a skill was partially installed (download interrupted), the manifest lists a script not in the repo, or the user manually deleted the file.","commonSituations":"Skill install was interrupted leaving skills/{name}/ but not scripts/; the skill author renamed a script but did not update the manifest; the user is on Windows and scriptName includes an extension the skill does not ship (e.g. .sh vs .ps1).","solutions":["Reinstall the skill so scripts/ is fully populated.","Verify the script filename (including extension) matches what the skill's manifest declares.","List the installed scripts directory to see what is actually present before invoking."],"exampleFix":"// before\nif (!fs.existsSync(scriptPath)) {\n  throw new Error(`Script not found: ${scriptName}`)\n}\n\n// after — list available scripts so the caller can self-correct\nif (!fs.existsSync(scriptPath)) {\n  const dir = path.join(skillsDir, skillName, 'scripts')\n  const available = fs.existsSync(dir) ? fs.readdirSync(dir).join(', ') : '(no scripts dir)'\n  throw new Error(`Script not found: ${scriptName}. Available: ${available}`)\n}","handlingStrategy":"validation","validationCode":"import fs from 'node:fs'\nimport path from 'node:path'\nfunction scriptExists(skillsDir: string, skillName: string, scriptName: string): boolean {\n  return fs.existsSync(path.join(skillsDir, skillName, 'scripts', scriptName))\n}\nif (!scriptExists(skillsDir, skillName, scriptName)) {\n  showToast(`Script ${scriptName} is not installed`)\n}","typeGuard":null,"tryCatchPattern":"const result = await ipcRenderer.invoke('skills:execute-script', params)\nif (!result.success && /Script not found/i.test(result.stderr)) {\n  // Refresh the script list and prompt reinstall\n  await refreshScriptsForSkill(skillName)\n}","preventionTips":["Reinstall the skill if scripts/ is missing files listed in the manifest.","Verify the script filename including extension (.sh vs .ps1) matches the manifest.","Surface the available scripts in the error so the caller can self-correct."],"tags":["filesystem","skills","ipc","missing-file","validation"],"backgroundTag":null,"analyzedSha":"81571269addb6bafb589a920b2883f1e1e084fd1","analyzedAt":"2026-08-12T21:51:44.981Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}