ruvnet/ruflo · error · Error
Validation failed: ${result.error}
Error message
Validation failed: ${result.error} What it means
Thrown by assertValid when a ValidationResult has valid=false. assertValid is the fail-fast bridge over the input-sanitization layer: it propagates the specific sub-error string (e.g. a null byte in a field, a disallowed value) via the interpolated message. It exists to turn soft validation failures into hard exceptions at trust boundaries.
Source
Thrown at v3/@claude-flow/cli-core/src/mcp-tools/validate-input.ts:201
return { valid: false, sanitized: {}, error: `${label}["${name}"] must be a string` };
}
if (rawVal.length > 32_768) {
return { valid: false, sanitized: {}, error: `${label}["${name}"] exceeds 32768 characters` };
}
if (rawVal.includes('\0')) {
return { valid: false, sanitized: {}, error: `${label}["${name}"] contains a null byte` };
}
out[name] = rawVal;
}
return { valid: true, sanitized: out };
}
/**
* Assert validation or throw with a structured error.
*/
export function assertValid(result: ValidationResult): string {
if (!result.valid) {
throw new Error(`Validation failed: ${result.error}`);
}
return result.sanitized;
}
// Try to load the full @claude-flow/security module for enhanced validation
// eslint-disable-next-line @typescript-eslint/no-explicit-any
let _securityModule: Record<string, any> | null = null;
let _securityLoaded = false;
async function getSecurityModule(): Promise<Record<string, any> | null> {
if (_securityLoaded) return _securityModule;
_securityLoaded = true;
try {
// Dynamic import — @claude-flow/security is an optional dependency
_securityModule = await (Function('return import("@claude-flow/security")')() as Promise<Record<string, any>>);
} catch {
// @claude-flow/security is optional — fallback to inline validation above
}View on GitHub (pinned to 6b01dc5a68)
Solutions
- Inspect the full interpolated message — the suffix after 'Validation failed:' is the precise field and reason (e.g. label["name"] contains a null byte).
- Fix the input at the source: strip/normalize control characters or supply the missing required field before re-validating.
- If running a server, return a 400 with result.error to the client rather than letting assertValid throw an unhandled exception.
- Run validateInput separately and branch on result.valid so you can collect all field errors instead of failing on the first one.
Example fix
// before
const sanitized = assertValid(validateInput(params));
// after
const result = validateInput(params);
if (!result.valid) {
return { status: 400, error: result.error };
}
const sanitized = result.sanitized; Defensive patterns
Strategy: validation
Validate before calling
const result = validateInput(params);
if (!result.valid) {
return { status: 400, error: result.error };
}
// only now is it safe to assertValid / use sanitized
const sanitized = result.sanitized; Type guard
function isValidationOk(r: ValidationResult): r is { valid: true; sanitized: Record<string, unknown> } {
return r.valid === true;
} Try / catch
try {
assertValid(result);
} catch (e) {
// e.message === `Validation failed: ${result.error}`
return { status: 400, error: (e as Error).message };
} Prevention
- Run validateInput and branch on result.valid rather than always asserting.
- Sanitize untrusted input (strip null bytes) before validation if clients are noisy.
- Return result.error to clients as a 400 instead of letting it throw.
- Keep validator schemas in lockstep with the client payload version.
When it happens
Trigger: Calling assertValid(result) where result came from a validate* function that detected unsafe input — a property containing a null byte, a value failing schema/type rules, or an unknown/extra field under strict mode. The message carries result.error verbatim, so the root cause is in the original validation result.
Common situations: Processing untrusted MCP/tool input that contains control characters (null bytes, odd unicode), client payloads with missing required fields, or version skew where a newer client sends fields the validator rejects. Common at the entry point of MCP tools that call assertValid immediately after validateInput.
Related errors
- Header JSON exceeds maximum size (${headerLen} > ${MAX_HEADE
- Path contains null bytes
- pod-template at ${path}: allowedMcpTools entries must be non
- pod-template at ${path}: allowedMcpTools must have ≥1 entry
- Invalid embedding model name: ${embeddingModel}
AI-assisted analysis of ruvnet/ruflo@6b01dc5a68 (2026-08-12).
Data as JSON: /api/errors/068badbee170b383.
Report an issue: GitHub.