nextlevelbuilder/ui-ux-pro-max-skill · error · Error

Failed to extract zip: ${error}

Error message

Failed to extract zip: ${error}

What it means

extractZip() shells out to the platform's unzip tool (Expand-Archive on Windows, the `unzip` binary elsewhere) and wraps any non-zero exit in a generic 'Failed to extract zip' error. It fires when the external command itself fails: the tool is missing, the archive is corrupt or not a real zip, or the path contains characters the shell command mangles.

Source

Thrown at cli/src/utils/extract.ts:22

import { promisify } from 'node:util';
import { tmpdir } from 'node:os';
import type { AIType } from '../types/index.js';
import { AI_FOLDERS } from '../types/index.js';

const execAsync = promisify(exec);

const EXCLUDED_FILES = ['settings.local.json'];

export async function extractZip(zipPath: string, destDir: string): Promise<void> {
  try {
    const isWindows = process.platform === 'win32';
    if (isWindows) {
      await execAsync(`powershell -Command "Expand-Archive -Path '${zipPath}' -DestinationPath '${destDir}' -Force"`);
    } else {
      await execAsync(`unzip -o "${zipPath}" -d "${destDir}"`);
    }
  } catch (error) {
    throw new Error(`Failed to extract zip: ${error}`);
  }
}

async function exists(path: string): Promise<boolean> {
  try {
    await access(path);
    return true;
  } catch {
    return false;
  }
}

export async function copyFolders(
  sourceDir: string,
  targetDir: string,
  aiType: AIType
): Promise<string[]> {
  const copiedFolders: string[] = [];

View on GitHub (pinned to a38d04c3d5)

Solutions

  1. Verify the downloaded file is a real zip: `file release.zip` or check its first bytes are 'PK'.
  2. Install the missing tool: `apt-get install unzip` (Debian/Alpine: `apk add unzip`) on Linux, or confirm PowerShell 5+ on Windows.
  3. Re-run the install on a stable network, or retry from a different network if the download was truncated.
  4. If the temp path contains quotes/spaces, move the operation to a plain path (e.g. TMPDIR without spaces) or replace execAsync with a Node zip library such as yauzl/adm-zip that takes paths as arguments, avoiding shell interpolation entirely.

Example fix

// before
await execAsync(`unzip -o "${zipPath}" -d "${destDir}"`);
// after: no shell interpolation, no external binary
import AdmZip from 'adm-zip';
new AdmZip(zipPath).extractAllTo(destDir, true);
Defensive patterns

Strategy: validation

Validate before calling

import { open } from 'node:fs/promises';
import { exec } from 'node:child_process';

async function isRealZip(path: string): Promise<boolean> {
  const fh = await open(path, 'r');
  try {
    const buf = Buffer.alloc(2);
    await fh.read(buf, 0, 2, 0);
    return buf.toString('ascii') === 'PK';
  } finally {
    await fh.close();
  }
}

function hasUnzip(): Promise<boolean> {
  return new Promise(res => exec('unzip -v', err => res(!err)));
}

Try / catch

try {
  await extractZip(zipPath, destDir);
} catch (e) {
  throw new Error(
    `Extraction failed on ${process.platform}; zip valid=${await isRealZip(zipPath)}; ` +
    `likely missing 'unzip' binary or corrupt download: ${(e as Error).message}`
  );
}

Prevention

When it happens

Trigger: `uipro init` reaches the extraction step and: (1) the downloaded release.zip is an HTML error page or truncated because the download was interrupted; (2) on macOS/Linux the `unzip` binary is not installed (minimal containers like alpine node images); (3) a path with spaces/quotes breaks the interpolated shell string; (4) Expand-Archive is unavailable on old Windows PowerShell or the destination is locked.

Common situations: Running the installer inside a slim Docker image that lacks unzip; a proxy/CDN returning a 200 response with an error body; extracting into a directory the user cannot write; zip64 archives that some unzip builds cannot handle.

Related errors


AI-assisted analysis of nextlevelbuilder/ui-ux-pro-max-skill@a38d04c3d5 (2026-08-14). Data as JSON: /api/errors/be74380e889e2b84. Report an issue: GitHub.