@photostructure/fs-metadata
    Preparing search index...

    Gotchas and Platform-Specific Issues

    This guide covers common issues, platform quirks, and important considerations when using @photostructure/fs-metadata.

    Problem: Linux, macOS, and Windows can block system calls indefinitely when network filesystems are unhealthy.

    Solution: Always use timeouts for network volumes:

    // Default timeout may be too short for network drives
    const metadata = await getVolumeMetadata("\\\\nas\\share", {
    timeoutMs: 30000, // 30 seconds
    });

    Why it happens:

    • Windows: SMB shares can block when the remote host is down
    • Linux: NFS mounts without soft option will retry forever
    • macOS: AFP/SMB shares may hang during network interruptions

    Optical drives (CD/DVD) can take 30+ seconds to spin up:

    const metadata = await getVolumeMetadata("D:\\", {
    timeoutMs: 45000, // 45 seconds for optical drives
    });

    Mapped network drives may not appear in volume listings:

    // This might not show mapped drives
    const volumes = await getVolumeMountPoints();

    // Use UNC path directly instead
    const metadata = await getVolumeMetadata("\\\\server\\share");

    C: is both a system volume and user storage. The library returns it in all queries:

    const volumes = await getAllVolumeMetadata({ includeSystemVolumes: false });
    // C:\ will still be included on Windows

    If you see "No Target Architecture" errors when building from source, ensure Visual Studio build tools are properly installed. See Windows Build Guide.

    The node:20 Docker image is not supported due to GLIBC version requirements:

    # ❌ Won't work
    FROM node:20

    # ✅ Use this instead
    FROM node:20-bullseye
    # or
    FROM debian:bullseye
    RUN apt-get update && apt-get install -y nodejs npm

    Many mount points on Linux are system-only:

    // This filters out /proc, /sys, /dev, snap mounts, etc.
    const userVolumes = await getAllVolumeMetadata({ includeSystemVolumes: false });

    // To see everything:
    const allVolumes = await getAllVolumeMetadata({ includeSystemVolumes: true });

    User-mounted volumes (like Google Drive, SMB shares via Nautilus) appear under /run/user/*/gvfs:

    const volumes = await getVolumeMountPoints();
    // May include entries like:
    // /run/user/1000/gvfs/smb-share:server=nas,share=documents

    APFS volumes in the same container share space:

    const volumes = await getAllVolumeMetadata();
    // Multiple volumes might report identical 'available' space
    // because they share the same APFS container

    Memory debugging tools like AddressSanitizer may fail due to SIP:

    # This might not work on macOS with SIP enabled
    ASAN_OPTIONS=detect_leaks=1 npm test

    Hidden file operations behave differently per platform:

    // On Windows: Sets hidden attribute
    await setHidden("C:\\file.txt", true);
    // File remains at: C:\file.txt (hidden)

    // On Linux/macOS: Renames file
    await setHidden("/home/user/file.txt", true);
    // File moved to: /home/user/.file.txt

    Setting an already-hidden file to hidden is a no-op:

    // No error, no change
    await setHidden("/path/to/.hidden", true);

    Dot-prefixing can create invalid paths:

    // This will fail - can't hide root directory
    await setHidden("/", true); // Error!

    // This will fail - parent directory in path
    await setHidden("/path/../file", true); // Error!

    Each volume check uses a separate thread on Windows. With many volumes:

    // This creates one thread per volume
    const volumes = await getVolumeMountPoints();

    // For systems with many volumes, use smaller batches
    const BATCH_SIZE = 10;
    for (let i = 0; i < mountPoints.length; i += BATCH_SIZE) {
    const batch = mountPoints.slice(i, i + BATCH_SIZE);
    await Promise.all(batch.map((mp) => getVolumeMetadata(mp.mountPoint)));
    }

    Tests can fail due to file system state changes:

    // ❌ Bad: Assumes exact values
    expect(metadata.available).toBe(1000000);

    // ✅ Good: Checks types and ranges
    expect(typeof metadata.available).toBe("number");
    expect(metadata.available).toBeGreaterThanOrEqual(0);

    File operations may not be immediately visible:

    // After creating a file
    await fs.writeFile(path, data);
    // May need a small delay on some systems
    await new Promise((resolve) => setTimeout(resolve, 10));
    const hidden = await isHidden(path);

    What works on one platform may fail on another:

    // Windows: This is valid
    const metadata = await getVolumeMetadata("C:"); // No trailing slash

    // Linux/macOS: Must have trailing slash
    const metadata2 = await getVolumeMetadata("/"); // Trailing slash required
    Error Cause Solution
    ENOENT Path doesn't exist Check path exists before calling
    EACCES Permission denied Run with appropriate permissions
    ETIMEDOUT Operation timed out Increase timeoutMs option
    Invalid mountPoint Empty or invalid path Validate input before calling
    statvfs failed Linux filesystem issue Check mount is accessible
    GetVolumeInformation failed Windows API error Verify drive letter is correct
    try {
    await getVolumeMetadata(mountPoint);
    } catch (error) {
    if (
    process.platform === "win32" &&
    error.message.includes("cannot find the path")
    ) {
    // Windows-specific path error
    } else if (
    process.platform === "linux" &&
    error.message.includes("statvfs")
    ) {
    // Linux-specific filesystem error
    } else if (error.code === "ETIMEDOUT") {
    // Cross-platform timeout
    }
    }

    Enable debug logging to troubleshoot issues:

    # Linux/macOS
    NODE_DEBUG=fs-meta npm test

    # Windows
    set NODE_DEBUG=fs-meta && npm test

    Debug output includes:

    • Native API calls and their results
    • Timeout occurrences
    • Thread creation/destruction (Windows)
    • Memory allocation tracking (debug builds)