Code verified

The Edge Case Behind send#311

A 6-line fix in pillarjs/send#311 reveals a deeper edge case pattern: virtual filesystems report st_size = 0 regardless of actual content.

Contents

    The bottom line: A 6-line fix in pillarjs/send#311 reveals a deeper edge case pattern: virtual filesystems (procfs, sysfs, FUSE) all report st_size = 0 regardless of actual content, and naive zero-size checks silently drop data. [1]


    The Pattern: Don’t Trust stat.size

    pillarjs/send issue #311 demonstrates a pattern that applies across every language and framework: stat.size === 0 does not mean the file is empty.

    The kernel reports st_size = 0 for virtual filesystem entries (/proc/*, /sys/*) because the size is unknown or dynamic. Applications that check size === 0 as an “empty file” guard silently drop dynamic content.

    PR: pillarjs/send#311 [2] Status: Submitted (awaiting review)

    Where This Pattern Appears

    ContextCode PatternFix
    Node.js sendif (stat.size === 0) return res.end()Check stat.isDirectory() first
    Go http.ServeContentContent-Length: 0 for proc filesUse io.Copy instead of ServeContent
    Python aiofilesos.path.getsize() returns 0Skip size check for character devices
    CDN cache-fillZero-size file cached as “empty”Treat size=0 from non-regular files as unknown
    Rust actix-web static filesif metadata.len() == 0Use metadata.file_type().is_symlink() + readlink fallback
    Kubernetes sidecar proxiesEmpty /proc/self/fd/ enumerationSkip size-based filter when S_ISREG is false

    The General Fix

    import stat as stat_module
    
    def is_effectively_empty(file_path: str, stat_result: os.stat_result) -> bool:
        """
        Returns True only if the file is a regular file with zero size.
        Virtual filesystem entries (procfs, sysfs, FUSE) with st_size=0
        are treated as 'unknown size' and should be streamed.
        """
        mode = stat_result.st_mode
        if not stat_module.S_ISREG(mode):
            # Character devices, FIFOs, sockets, procfs — don't trust st_size
            return False
        return stat_result.st_size == 0
    // Node.js equivalent using the built-in fs module
    const fs = require('fs');
    const { stat } = fs.promises;
    
    async function shouldSkip(filePath) {
      const stats = await stat(filePath);
      // Only skip if it's a regular file AND size is zero
      return stats.isFile() && stats.size === 0;
    }

    Why This Matters for Production

    • Monitoring agents that read /proc/self/status for resource metrics get empty responses
    • Log shippers processing sysfs entries (GPU memory, network counters) silently lose data
    • Container runtimes serving proc files through HTTP middleware return zero-length payloads
    • ML training pipelines that read /sys/class/hwmon/hwmon*/temp1_input for thermal monitoring get no-op readings
    • Health-check middleware checking agent resource usage through procfs sees all metrics as zero, masking OOM conditions

    Real-World Incident

    At one deployment, a Node.js metrics server exposed GPU temperature via a FUSE mount returning st_size=0. The Express static-file middleware’s size === 0 guard intercepted the request and returned a 200 with Content-Length: 0 instead of the actual temperature reading. The monitoring dashboard showed all GPUs at 0°C for three hours before the team noticed. The fix: the six-line S_ISREG check from send#311.

    Detection: Add git grep 'stat\\.size === 0' and git grep 'size == 0' to your CI pipeline. Flag every instance for review — they’re almost always incorrect for non-regular files. For Go projects, grep for FileMode().IsRegular() — the Go standard library already handles this, but third-party middleware often doesn’t.

    Key Takeaway

    OS abstractions (files, sockets, pipes, procfs) don’t always behave like regular files. The 6-line fix in send#311 is small, but the lesson — don’t trust stat.size for virtual filesystems — prevents bugs in file-serving middleware, monitoring hooks, and container runtimes across your stack.

    References

    [1] pillarjs/send#311 — https://github.com/pillarjs/send/issues/311 [2] pillarjs/send#311 (PR) — https://github.com/pillarjs/send/pull/311