ruvnet/ruflo · error · Error

Invalid argument: contains shell metacharacters

Error message

Invalid argument: contains shell metacharacters

What it means

execNpmCommand() rejects any argument containing ; & | ` $ ( ) < > before spawning npm — even though it uses execFileSync with shell: false. It is a defense-in-depth injection guard, so values that are harmless under shell:false but merely contain those characters (Windows paths with parentheses, decorated dist-tags) are rejected too.

Source

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

      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,
        stdio: returnOutput ? ['pipe', 'pipe', 'pipe'] : 'inherit'
      });
      return returnOutput ? output : '';
    } catch (error) {
      throw error;
    }
  }

  /**
   * Execute command (for build scripts only - validated)
   */
  private execCommand(cmd: string, returnOutput = false): string {

View on GitHub (pinned to fa13ee4ad6)

Solutions

  1. Restrict tag and outputDir values to [A-Za-z0-9._-]: use 'beta-1' not 'beta (1)', 'next-rc1' not 'next$(date)'
  2. Choose an outputDir without parentheses or special characters, or omit outputDir and move the resulting tarball yourself
  3. Pre-validate with the same class /[;&|`$()<>]/ before calling pack/publishToNpm so you control the error message

Example fix

// before
await publisher.publishToNpm({ tag: 'release (main)' }); // throws: shell metacharacters

// after
await publisher.publishToNpm({ tag: 'release-main' });
Defensive patterns

Strategy: validation

Validate before calling

const METACHARS = /[;&|`$()<>]/;
function isSafeNpmArg(value: string): boolean {
  return typeof value === 'string' && value.length > 0 && !METACHARS.test(value);
}
if (!isSafeNpmArg(tag)) throw new Error('tag contains shell metacharacters');
await publisher.publishToNpm({ tag });

Prevention

When it happens

Trigger: publishToNpm({ tag: 'next$(whoami)' }) or any tag/registry/otp string containing ()<>|&`$; pack('dir (1)') with parentheses in the outputDir; branch-derived tag names like 'feature/fix-(auth)'.

Common situations: Windows directories with parentheses (e.g. 'Dir (1)'); automated tag names built from branch or PR titles; release scripts interpolating versions like '1.0.0-beta(1)'.

Related errors


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