macOS: default `projects_dir` is placed inside `~/Documents`, which iCloud syncs
Verified against: PlatformIO Core 6.1.19, PlatformIO IDE for VSCode 3.3.4, macOS 26.5.2
Summary
On macOS, get_default_projects_dir() unconditionally resolves to
~/Documents/PlatformIO/Projects. When a user has iCloud Drive's
"Desktop & Documents Folders" enabled — an option macOS actively prompts for during setup —
that directory is managed by fileproviderd. PlatformIO then writes .pio/ build trees,
hundreds of MB of churn per rebuild, into a live sync target. This produces build and
toolchain failures that are hard to attribute, because the sync daemon acts after the
operation reports success.
platformio/project/helpers.py:
def get_default_projects_dir():
docs_dir = os.path.join(fs.expanduser("~"), "Documents")
try:
assert IS_WINDOWS
...
except:
if not IS_MACOS:
try:
docs_dir = subprocess.check_output(["xdg-user-dir", "DOCUMENTS"]) ...macOS is the one platform with no path to a different answer: the Windows branch is behind
assert IS_WINDOWS, and the xdg-user-dir branch is explicitly excluded by if not IS_MACOS.
Impact observed
An ESP32 project living at the default location hit three independent failure classes, all
traced to FileProvider and all resolved by moving the repo out of ~/Documents:
- Code signing broken by xattr churn — FileProvider re-stamps extended attributes faster than they can be cleared, so signing an artifact built in-tree fails intermittently.
- Duplicate symbols at link time — iCloud conflict copies (
provision 2.cppbesideprovision.cpp) are picked up bybuild_src_filterglobs and compiled as real sources. managed_componentseviction races — the component manager failed with "CHANGELOG.md missing" andrmtree: Directory not emptythree times in one night while iCloud evicted and re-materialised that directory mid-build.
Plus the silent cost: every hybrid rebuild pushes hundreds of MB of .pio churn through the
user's iCloud quota and uplink.
Why the existing mitigations do not cover this
platformio-core#4497(OneDrive, closed in 6.1.7) hardened.pio/buildremoval. That is the symptom; the default location is the cause, and it is still cloud-synced.- The setting is changeable —
pio settings set projects_dir <path>— but there is no UI for it anywhere. PIO Home readscoreSettingsand cannot write them:AppRPC.load_staterebuilds them per call and setsstate.modified = False, the frontend deletesstorage.coreSettingsbeforeapp.save_state, and no settings-write RPC handler exists. The VSCode extension contributes no corresponding configuration key either. So a user who hits this must discover a CLI command that the GUI never mentions — which is consistent with the number of forum threads asking exactly this question.
Suggested fix
Any one of these would close it; they are listed cheapest-first.
- Detect and avoid. On macOS, check whether
~/Documentsis under FileProvider management before using it as the default, and fall back to~/PlatformIO/Projects. A cheap signal is the presence of~/Library/Mobile Documents/com~apple~CloudDocsplus~/Documentsresolving into the FileProvider tree. - Change the macOS default outright to
~/PlatformIO/Projects(or~/Library/Application Support/PlatformIO/Projects). Existing installs are unaffected — they carry an explicit value inappstate.json. - At minimum, warn once. If a build's project path is inside a synced directory, emit a
one-line warning naming the risk and the
pio settings set projects_dircommand. This alone would have saved the debugging described above.
Additionally, surfacing projects_dir in PIO Home's UI would make (3) actionable at the
moment the user reads it.
Workaround for anyone who finds this issue
pio settings set projects_dir ~/YourProjects # persists in ~/.platformio/appstate.json
export PLATFORMIO_SETTING_PROJECTS_DIR=~/YourProjects # wins over the above; survives a settings resetNote the env var is PLATFORMIO_SETTING_<NAME>, not PLATFORMIO_<NAME>. The path must
already exist — projects_dir_validate asserts os.path.isdir on every read.
Source: platformio/platformio-core