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

  1. Strip or reject NUL bytes at the source: sanitize inputs with path.replace(/\0/g, '') or refuse them before invoking the daemon command
  2. Fix the upstream producer (CI variable, env file, script) that introduced the \0 — this error means your input pipeline is already corrupt
  3. 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

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


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)