ruvnet/ruflo · error · ExtractionError

tar extraction failed

Error message

tar extraction failed (exit ${result.exitCode}): ${result.stderr || result.stdout}

What it means

During meta-proxy binary installation, .tar.gz (and zip-fallback) extraction shells out to `tar` through SafeExecutor (60s timeout, allowlist ['tar']). A non-zero exit code throws ExtractionError with the code plus tar's stderr/stdout. The archive was downloaded into a work directory first, so the failure is about extraction, not the download itself — typically a truncated/corrupt archive, missing tar binary, disk full, or permission problems on the extract directory.

Solutions

  1. Read the embedded stderr in the message — tar states the exact problem (corrupt gzip, permissions, space).
  2. Free disk space / fix permissions on the install work directory, then re-run install (it re-downloads).
  3. Ensure `tar` exists on PATH (Windows 10+ ships bsdtar; Linux/macOS always do; slim containers need the tar package added).
  4. If corruption is suspected, clear the installer's work/cache directory so a fresh archive is downloaded.

Example fix

# before: slim container without tar -> exit 127 -> ExtractionError
FROM node:22-slim
RUN claude-flow-proxy-install

# after
FROM node:22-slim
RUN apt-get update && apt-get install -y tar && rm -rf /var/lib/apt/lists/*
RUN claude-flow-proxy-install
Defensive patterns

Strategy: retry

Validate before calling

import { execFileSync } from 'node:child_process';
function tarAvailable(): boolean {
  try { execFileSync('tar', ['--version'], { stdio: 'ignore' }); return true; } catch { return false; }
}
// Before triggering proxy install:
if (!tarAvailable()) throw new Error('tar not on PATH — install it (apt-get install tar / use Windows 10+) before proxy install');

Type guard

function isExtractionError(e: unknown): e is Error {
  return e instanceof Error && /tar extraction failed \(exit \d+\)/.test(e.message);
}

Try / catch

for (let attempt = 1; ; attempt++) {
  try {
    return await installProxyBinary({ version });
  } catch (e) {
    if (attempt < 3 && isExtractionError(e)) {
      await cleanWorkDir(); // drop the possibly-truncated archive so the retry re-downloads
      continue;
    }
    if (isExtractionError(e)) throw new Error(`proxy install failed after retries: ${e.message}`);
    throw e;
  }
}

Prevention

When it happens

Trigger: install runs `tar xzf <archive> -C <extractDir>` and tar exits non-zero: gzip corruption from a partially downloaded asset ('unexpected end of file'), 'tar: command not found' variant (exit 127 via executor), no space left on device, read-only extract dir, or extraction exceeding the 60s SafeExecutor timeout on huge archives/slow disks.

Common situations: Flaky network producing truncated downloads; minimal Docker images (slim/distroless) without tar; CI runners with full disks; Windows boxes older than Windows 10 (no bsdtar); aggressive antivirus locking the archive mid-extraction on Windows.

Related errors


AI-assisted analysis of ruvnet/ruflo@29f048fc3b (2026-08-18). Data as JSON: /api/errors/48877b9bae574aae. Report an issue: GitHub.

Appendix: source

Thrown at v3/@claude-flow/cli/src/proxy/install.ts:36

export class ExtractionError extends Error {
  constructor(message: string) {
    super(message);
    this.name = 'ExtractionError';
  }
}

function binaryNameInArchive(): string {
  return process.platform === 'win32' ? 'meta-proxy.exe' : 'meta-proxy';
}

/** `tar` handles `.tar.gz` everywhere, and `.zip` via bsdtar on Windows 10+. */
async function extractWithTar(archivePath: string, extractDir: string, flags: 'xzf' | 'xf'): Promise<void> {
  const { SafeExecutor } = await import('@claude-flow/security');
  const exec = new SafeExecutor({ allowedCommands: ['tar'], timeout: 60_000 });
  const result = await exec.execute('tar', [flags, archivePath, '-C', extractDir]);
  if (result.exitCode !== 0) {
    throw new ExtractionError(`tar extraction failed (exit ${result.exitCode}): ${result.stderr || result.stdout}`);
  }
}

/**
 * Single-quoted literal paths (doubling any embedded single quote per
 * PowerShell string-literal escaping) passed as ONE argv element to
 * `-Command`. shell:false means no OS shell ever tokenizes this string —
 * only powershell.exe's own parser does.
 */
async function extractWithPowerShell(archivePath: string, extractDir: string): Promise<void> {
  const { SafeExecutor } = await import('@claude-flow/security');
  const escape = (p: string) => p.replace(/'/g, "''");
  const command = `Expand-Archive -LiteralPath '${escape(archivePath)}' -DestinationPath '${escape(extractDir)}' -Force`;
  const exec = new SafeExecutor({ allowedCommands: ['powershell', 'powershell.exe'], timeout: 60_000 });
  const result = await exec.execute('powershell', ['-NoProfile', '-NonInteractive', '-Command', command]);
  if (result.exitCode !== 0) {
    throw new ExtractionError(`Expand-Archive failed (exit ${result.exitCode}): ${result.stderr || result.stdout}`);
  }

View on GitHub (pinned to 29f048fc3b)