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
- 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.
- 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.
- Switch to the non-throwing validate() call and branch on result.errors for expected denials.
- 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
- Prefer validate() over validateOrThrow() for user-supplied paths — denial is an expected outcome, not an exception.
- On macOS/symlinked layouts, feed fs.realpath'd workspace roots as allowedPrefixes (see #3010).
- Log result.errors verbatim; each clause names the exact rule (prefix/extension/name/length/hidden) that tripped.
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
- EMPTY_PREFIXES
- localSingleEntryPageRank: sourceIndex ${src} out of range [0
- Validation failed: ${result.error}
- ${toolsJson} must contain a JSON array of {name, description
- basePath contains disallowed characters
AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18).
Data as JSON: /api/errors/1a40dfaf2adff938.
Report an issue: GitHub.