chatboxai/chatbox · warning · Error

Skill name and script name are required

Error message

Skill name and script name are required

What it means

Thrown by the skills:execute-script IPC handler when skillName or scriptName is falsy (empty string, undefined, null). This is the first guard before any filesystem access, validating that the renderer sent both required identifiers. The handler signature types them as string, but IPC payloads cross the trust boundary and are not guaranteed to match the declared type.

Source

Thrown at src/main/skills/ipc-handlers.ts:174

      await shell.openPath(skillsDir)
      return { success: true }
    } catch (error) {
      log.error('skills:open-directory failed', error)
      return { success: false, error: error instanceof Error ? error.message : 'Unknown error' }
    }
  })

  ipcMain.handle(
    'skills:execute-script',
    async (
      _event,
      params: { skillName: string; scriptName: string; args?: string[] }
    ): Promise<{ success: boolean; stdout: string; stderr: string; exitCode: number | null }> => {
      const { skillName, scriptName, args = [] } = params

      try {
        if (!skillName || !scriptName) {
          throw new Error('Skill name and script name are required')
        }

        if (skillName.includes('..') || skillName.includes('/') || skillName.includes('\\')) {
          throw new Error('Invalid skill name: path traversal not allowed')
        }

        if (scriptName.includes('..') || scriptName.includes('/') || scriptName.includes('\\')) {
          throw new Error('Invalid script name: path traversal not allowed')
        }

        const skillsDir = getSkillsDir()
        const scriptPath = path.join(skillsDir, skillName, 'scripts', scriptName)
        if (!fs.existsSync(scriptPath)) {
          throw new Error(`Script not found: ${scriptName}`)
        }
        const resolvedSkillsDir = fs.realpathSync(skillsDir)
        const resolvedScriptPath = fs.realpathSync(scriptPath)
        if (!resolvedScriptPath.startsWith(`${resolvedSkillsDir}${path.sep}`)) {

View on GitHub (pinned to 81571269ad)

Solutions

  1. Inspect the IPC call site in the renderer to confirm both skillName and scriptName are non-empty strings before invoking.
  2. Add a defensive guard in the renderer that disables the run button when either field is empty.
  3. If using a custom integration, validate the params object shape against the handler's expected type before send.

Example fix

// before
if (!skillName || !scriptName) {
  throw new Error('Skill name and script name are required')
}

// renderer-side guard
if (!skillName?.trim() || !scriptName?.trim()) {
  showToast('Skill name and script name are required')
  return
}
await window.electron.ipcRenderer.invoke('skills:execute-script', { skillName, scriptName, args })
Defensive patterns

Strategy: validation

Validate before calling

function validateExecuteScriptParams(params: unknown): params is { skillName: string; scriptName: string; args?: string[] } {
  if (!params || typeof params !== 'object') return false
  const p = params as any
  return typeof p.skillName === 'string' && p.skillName.trim().length > 0
    && typeof p.scriptName === 'string' && p.scriptName.trim().length > 0
    && (p.args === undefined || Array.isArray(p.args))
}
if (!validateExecuteScriptParams(params)) throw new Error('Invalid params')

Type guard

function hasRequiredFields(p: unknown): p is { skillName: string; scriptName: string } {
  return typeof (p as any)?.skillName === 'string' && !!((p as any).skillName).trim()
    && typeof (p as any)?.scriptName === 'string' && !!((p as any).scriptName).trim()
}

Try / catch

ipcRenderer.invoke('skills:execute-script', params).then((res) => {
  if (!res.success) showError(res.stderr)
})
// handler already catches internally and returns { success: false, stderr: message }

Prevention

When it happens

Trigger: The renderer invokes ipcRenderer.invoke('skills:execute-script', { skillName: '', scriptName: 'foo.sh' }) or omits scriptName entirely. Happens with malformed IPC calls, a stale renderer bundle whose payload shape changed, or a bug in the calling code that constructs the params object.

Common situations: A skill card UI sends an empty skillName because the selected skill was uninstalled between render and click; a plugin/script-runner integration passes undefined because it reads from an optional field; version skew between main and renderer after a partial update.

Related errors


AI-assisted analysis of chatboxai/chatbox@81571269ad (2026-08-12). Data as JSON: /api/errors/b655b7d13df258ac. Report an issue: GitHub.