ruvnet/ruflo · error · PathValidatorError

VALIDATION_FAILED

VALIDATION_FAILED

Error message

result.errors.join('; ')

What it means

validateOrThrow() is the throwing variant of validate(): it runs the full path check (allowed-prefix containment, blocked extensions/names, length, hidden-file rules, symlink resolution) and throws PathValidatorError VALIDATION_FAILED with all collected errors joined by '; ' when any check fails. The offending inputPath is attached to the error.

Source

Thrown at v3/@claude-flow/security/src/path-validator.ts:465

      resolvedPath,
      relativePath,
      matchedPrefix,
      errors,
    };
  }

  /**
   * Validates and returns resolved path, throwing on failure.
   *
   * @param inputPath - The path to validate
   * @returns Resolved path if valid
   * @throws PathValidatorError if validation fails
   */
  async validateOrThrow(inputPath: string): Promise<string> {
    const result = await this.validate(inputPath);

    if (!result.isValid) {
      throw new PathValidatorError(
        result.errors.join('; '),
        'VALIDATION_FAILED',
        inputPath
      );
    }

    return result.resolvedPath;
  }

  /**
   * Synchronous validation (without symlink resolution).
   *
   * @param inputPath - The path to validate
   * @returns Validation result
   */
  validateSync(inputPath: string): PathValidationResult {
    const errors: string[] = [];

View on GitHub (pinned to fa13ee4ad6)

Solutions

  1. Parse the error message — each clause names the exact rule that failed, which tells you whether it is a prefix, extension, name, length, or symlink problem.
  2. On macOS or symlinked deployments, add the realpath'd prefix (fs.realpathSync on your root) to allowedPrefixes, since the constructor pre-resolves prefixes but validate() canonicalizes candidates.
  3. Switch to the non-throwing validate() call and branch on result.errors for expected denials.
  4. If a blocked name/extension must be accessible in your context, override blockedNames/blockedExtensions in the constructor config deliberately.

Example fix

// before
const safe = await validator.validateOrThrow(userPath); // throws on .env or symlink escapes

// after
const result = await validator.validate(userPath);
if (!result.isValid) return { allowed: false, reasons: result.errors };
const safe = result.resolvedPath;
Defensive patterns

Strategy: validation

Validate before calling

const result = await validator.validate(inputPath);
if (!result.isValid) {
  return { allowed: false, reasons: result.errors }; // graceful branch
}
const safePath = result.resolvedPath;

Try / catch

try {
  return await validator.validateOrThrow(inputPath);
} catch (err) {
  if (err instanceof PathValidatorError && err.code === 'VALIDATION_FAILED') {
    throw new ForbiddenAccess(err.message, { path: inputPath });
  }
  throw err;
}

Prevention

When it happens

Trigger: Validating '/etc/passwd' when allowedPrefixes is ['/tmp']; accessing '.env' or a blocked name from DEFAULT_BLOCKED_NAMES; a path longer than maxPathLength (4096); a hidden dotfile when allowHidden is false (the default); a symlink whose realpath lands outside every prefix — the classic macOS os.tmpdir() -> /private/var case documented for #3010.

Common situations: Workspaces living under symlinked directories on macOS; agents legitimately trying to read .env (blocked by design); writing generated files with blocked extensions; moving a project into a path reachable only through symlinks.

Related errors


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