{"record":{"id":"87de008d07a334bf","repo":"ArchiveBox/ArchiveBox","slug":"x-oserror-failed-to-write-path-with-fcntl-f-f","errorCode":null,"errorMessage":"[X] OSError: Failed to write {path} with fcntl.F_FULLFSYNC. ({e})","messagePattern":"\\[X\\] OSError: Failed to write (.+?) with fcntl\\.F_FULLFSYNC\\. \\((.+?)\\)","errorType":"console","errorClass":"SystemExit","httpStatus":null,"severity":"critical","filePath":"archivebox/misc/system.py","lineNumber":41,"sourceCode":"    encoding = None if isinstance(contents, bytes) else \"utf-8\"  # enforce utf-8 on all text writes\n\n    try:\n        with lib_atomic_write(path, mode=mode, overwrite=overwrite, encoding=encoding) as f:\n            if isinstance(contents, dict):\n                dump(contents, f, indent=4, sort_keys=True, cls=ExtendedEncoder)\n            elif isinstance(contents, (bytes, str)):\n                f.write(contents)\n    except OSError as e:\n        config = config or get_config(**config_kwargs)\n        if config.ENFORCE_ATOMIC_WRITES:\n            print(f\"[X] OSError: Failed to write {path} with fcntl.F_FULLFSYNC. ({e})\")\n            print(\n                \"    You can store the archive/ subfolder on a hard drive or network share that doesn't support support synchronous writes,\",\n            )\n            print(\n                \"    but the main folder containing the index.sqlite3 and ArchiveBox.conf files must be on a filesystem that supports FSYNC.\",\n            )\n            raise SystemExit(1)\n\n        # retry the write without forcing FSYNC (aka atomic mode)\n        with open(path, mode=mode, encoding=encoding) as f:\n            if isinstance(contents, dict):\n                dump(contents, f, indent=4, sort_keys=True, cls=ExtendedEncoder)\n            elif isinstance(contents, (bytes, str)):\n                f.write(contents)\n\n    # set file permissions\n    config = config or get_config(**config_kwargs)\n    os.chmod(path, int(config.OUTPUT_PERMISSIONS, base=8))\n\n\n@enforce_types\ndef get_dir_size(path: str | Path, recursive: bool = True, pattern: str | None = None) -> tuple[int, int, int]:\n    \"\"\"get the total disk size of a given directory, optionally summing up\n    recursively and limiting to a given filter list\n    \"\"\"","sourceCodeStart":23,"sourceCodeEnd":59,"githubUrl":"https://github.com/ArchiveBox/ArchiveBox/blob/74564b28220090664f919479e82cbf454125fa34/archivebox/misc/system.py#L23-L59","documentation":"atomic_write performs an fsync'd atomic write using fcntl F_FULLFSYNC (macOS/BSD). When the fsync syscall fails (e.g. filesystem doesn't support it, like some network shares/exFAT), the writer retries without F_FULLFSYNC only after printing the guidance that the main data dir must support FSYNC — the message with this text is the OSError report printed before that fallback/exit path, and it raises SystemExit(1) when writes can't be made durable.","triggerScenarios":"Calling atomic_write (directly or via write_config_file / write_json_details / write_html_details / _write_file_if_changed) with the data dir located on a filesystem where fcntl(fd, F_FULLFSYNC) fails: SMB/NFS shares, exFAT/FAT32 drives, certain virtualized mounts. Called during `archivebox init`, config updates, and snapshot detail writes.","commonSituations":"macOS users pointing DATA_DIR at an exFAT external drive or SMB network share; Docker volumes on filesystems lacking F_FULLFSYNC; moving index.sqlite3/ArchiveBox.conf storage to a NAS.","solutions":["Move DATA_DIR to a local filesystem that supports fsync (APFS/ext4); keep only archive/ on the external share.","Update macOS/Docker/VM so F_FULLFSYNC is supported by the underlying mount.","If only the fallback path matters, ensure the code's retry-without-FSYNC branch can write (check remaining permissions/space) — but note index/SQLite dirs must support FSYNC.","Report/inspect the underlying errno `e` in the message to identify the exact failing operation."],"exampleFix":"// before\nDATA_DIR=/Volumes/EXFAT_DRIVE/archivebox   # F_FULLFSYNC unsupported\n// after\nDATA_DIR=/Users/me/archivebox/data          # local APFS, fsync OK\n# keep /Volumes/EXFAT_DRIVE/archivebox/archive as an output dir instead","handlingStrategy":"validation","validationCode":"import fcntl\ndef fs_supports_fsync(dir_path: str) -> bool:\n    import os, tempfile\n    fd, tmp = tempfile.mkstemp(dir=dir_path)\n    try:\n        fcntl.fsync(fd)          # plain fsync\n        try:\n            fcntl.fcntl(fd, fcntl.F_FULLFSYNC)  # will raise on Linux/unsupported\n        except (AttributeError, OSError):\n            pass  # F_FULLFSYNC unavailable; atomic_write falls back\n        return True\n    except OSError:\n        return False\n    finally:\n        os.close(fd); os.remove(tmp)","typeGuard":null,"tryCatchPattern":"try:\n    atomic_write(path, contents)\nexcept SystemExit as e:\n    if e.code == 1:\n        log.critical(\"data dir filesystem does not support FSYNC; move DATA_DIR to a local fs\")\n    raise\nexcept OSError as e:\n    log.critical(f\"write failed: {e}\")\n    raise","preventionTips":["Keep index.sqlite3 and ArchiveBox.conf on a local fsync-capable filesystem","Put only archive/ output on NAS/external drives","Pre-validate new DATA_DIR locations with a small fsync probe before init","After permission/errno errors, fix ownership/space before retrying writes"],"tags":["filesystem","fsync","macos","durability"],"backgroundTag":"fsync-unsupported-filesystem","analyzedSha":"74564b28220090664f919479e82cbf454125fa34","analyzedAt":"2026-08-28T23:56:51.556Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}