ruvnet/ruflo · error · Error

Failed to pack: ${error}

Error message

Failed to pack: ${error}

What it means

Publisher.pack() runs `npm pack` (optionally with --pack-destination outputDir) and re-wraps any failure as 'Failed to pack: <cause>'. The underlying cause is visible in the wrapped message: npm missing from PATH, no package.json in cwd, or a pack-destination directory that does not exist.

Source

Thrown at v3/@claude-flow/deployment/src/publisher.ts:189

    }
  }

  /**
   * Pack package to tarball
   */
  async pack(outputDir?: string): Promise<string> {
    try {
      const packArgs = ['pack'];
      if (outputDir) {
        packArgs.push('--pack-destination', outputDir);
      }

      const output = this.execNpmCommand(packArgs, true);
      const tarballName = output.trim().split('\n').pop() || '';

      return outputDir ? join(outputDir, tarballName) : tarballName;
    } catch (error) {
      throw new Error(`Failed to pack: ${error}`);
    }
  }

  /**
   * Execute npm command safely using execFileSync
   */
  private execNpmCommand(args: string[], returnOutput = false): string {
    try {
      // Validate args don't contain shell metacharacters
      for (const arg of args) {
        if (/[;&|`$()<>]/.test(arg)) {
          throw new Error(`Invalid argument: contains shell metacharacters`);
        }
      }
      const output = execFileSync('npm', args, {
        cwd: this.cwd,
        encoding: 'utf-8',
        shell: false,

View on GitHub (pinned to fa13ee4ad6)

Solutions

  1. Create the destination first: mkdirSync(outputDir, { recursive: true }) before calling pack()
  2. Verify npm is on PATH in the environment that constructs the Publisher (execSync('npm --version'))
  3. Pass a simple absolute outputDir containing only metacharacter-free characters (no () & | < > ` $ ;)

Example fix

// before
const tarball = await publisher.pack(join(outRoot, 'dist-(1)')); // fails

// after
mkdirSync(join(outRoot, 'dist-1'), { recursive: true });
const tarball = await publisher.pack(join(outRoot, 'dist-1'));
Defensive patterns

Strategy: try-catch

Validate before calling

import { mkdirSync, existsSync } from 'node:fs';
import { execSync } from 'node:child_process';
if (outputDir) mkdirSync(outputDir, { recursive: true });
execSync('npm --version'); // fail fast if npm is missing
await publisher.pack(outputDir);

Try / catch

try {
  const tarball = await publisher.pack(outDir);
  if (!existsSync(tarball)) throw new Error(`pack reported ${tarball} but file is missing`);
} catch (e) {
  const msg = e instanceof Error ? e.message : String(e);
  if (msg.startsWith('Failed to pack:')) {
    // inspect the wrapped cause: ENOENT dir, npm missing, metacharacter guard
  }
  throw e;
}

Prevention

When it happens

Trigger: pack('/tmp/out') when /tmp/out does not exist (npm rejects --pack-destination pointing at a missing directory); npm not installed or not on PATH in the runtime container; cwd without a package.json; an argument rejected by the shell-metacharacter guard inside execNpmCommand (e.g. a destination containing parentheses).

Common situations: Minimal CI images without node/npm on PATH; passing a relative outputDir that resolves against an unexpected cwd; Windows-style paths with parentheses tripping the metacharacter check.

Related errors


AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18). Data as JSON: /api/errors/07866f16f85b03a4. Report an issue: GitHub.