ruvnet/ruflo · error · Error
contains null bytes
Error message
${label} contains null bytes What it means
validatePath() in daemon.ts is a security gate for paths the daemon command accepts (log/pid/workspace-related paths later embedded in a forked child's argv and used in filesystem calls). Any path containing a NUL byte (\0) is rejected before use — the classic null-byte injection defense against C-string truncation tricks where 'safe/../../evil\0' could be cut short by underlying native APIs. The thrown Error propagates up to daemon start's catch and prints 'Failed to start daemon: <label> contains null bytes'.
Solutions
- Strip or reject NUL bytes at the source: sanitize inputs with path.replace(/\0/g, '') or refuse them before invoking the daemon command
- Fix the upstream producer (CI variable, env file, script) that introduced the \0 — this error means your input pipeline is already corrupt
- Check the exact flag path in the error label to identify which argument carried the byte
Example fix
// before
const logFile = decodeURIComponent(process.env.DAEMON_LOG); // may contain %00 -> \0
await daemonStart({ logFile });
// after
const raw = decodeURIComponent(process.env.DAEMON_LOG ?? '');
if (raw.includes('\0')) throw new Error('DAEMON_LOG contains NUL byte');
const logFile = raw.replace(/\0/g, '');
await daemonStart({ logFile }); Defensive patterns
Strategy: validation
Validate before calling
function rejectNul(s: string, label: string): string {
if (s.includes('\0')) throw new Error(`${label} contains NUL byte`);
return s;
}
const logFile = rejectNul(decodeURIComponent(process.env.DAEMON_LOG ?? ''), 'DAEMON_LOG'); Type guard
function isNullByteFreePath(v: unknown): v is string {
return typeof v === 'string' && !v.includes('\0') && !/[;&|`$<>]/.test(v);
} Try / catch
try {
await startDaemon(projectRoot, config);
} catch (err) {
if (err instanceof Error && /contains null bytes/.test(err.message)) {
// input pipeline corruption — fix the source of the \0, do not retry as-is
}
} Prevention
- Sanitize every path assembled from env vars, CI parameters, or decoded URLs before passing it to daemon commands
- URL-decode then reject %00-derived NUL bytes at your system boundary
- Never retry the same input after this error — it indicates corrupted or hostile input, not a transient failure
When it happens
Trigger: Starting the daemon with a path flag or environment-derived path containing a literal NUL: e.g. --log-file '/var/log/d\0evil' or a %00 that was URL-decoded into \0 by a wrapper script. Only a string actually containing the \0 code point triggers it.
Common situations: CI/CD pipelines URL-decoding %00 from a parameter into the path; shell scripts with unescaped escape sequences; probing/malicious input fuzzing path flags; copy-paste from web content carrying hidden control characters.
Related errors
- contains shell metacharacters
- Event log path contains null bytes
- extracted binary path failed validation
- escapes project directory
- repository path escapes root
AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18).
Data as JSON: /api/errors/52c21d6d035c7aad.
Report an issue: GitHub.
Appendix: source
Thrown at v3/@claude-flow/cli/src/commands/daemon.ts:372
return { success: true };
} catch (error) {
output.printError(`Failed to start daemon: ${error instanceof Error ? error.message : String(error)}`);
return { success: false, exitCode: 1 };
}
},
};
/**
* Validate path for security - prevents path traversal and injection
*/
function validatePath(path: string, label: string): void {
// Must be absolute after resolution
const resolved = resolve(path);
// Check for null bytes (injection attack)
if (path.includes('\0')) {
throw new Error(`${label} contains null bytes`);
}
// Check for shell metacharacters in path components
if (/[;&|`$<>]/.test(path)) {
throw new Error(`${label} contains shell metacharacters`);
}
// Prevent path traversal outside expected directories
if (!resolved.includes('.claude-flow') && !resolved.includes('bin')) {
// Allow only paths within project structure
const cwd = process.cwd();
if (!resolved.startsWith(cwd)) {
throw new Error(`${label} escapes project directory`);
}
}
}
/**View on GitHub (pinned to fa13ee4ad6)