Distributed binaries always exit 0 on handled errors: starters/icloudpd.py calls cli() without sys.exit()
Overview
icloudpd exits with status 0 even when it fails with a handled error (connection error, API error, failed login, ...). The Python code returns the right codes, but the PyInstaller entry script discards them.
src/starters/icloudpd.py:
from icloudpd.cli import cli
if __name__ == "__main__":
cli() # return value (1 / 2) is dropped -> process exits 0cli() returns run_with_configs(...), which propagates core_single_run()'s return 1 for PyiCloudConnectionErrorException, PyiCloudAPIResponseException, PyiCloudFailedLoginException, etc., and return 2 for argument validation errors — but nothing calls sys.exit() on that value, so the exit status is always 0 unless an unhandled exception escapes.
This affects everything built from that starter: the PyPI wheels (the console script icloudpd → icloudpd/__main__.py → subprocess.call(<bundled binary>) correctly propagates the binary's code, but the binary itself is already 0), the GitHub release binaries, and the Docker image. A source/editable install using the [project.scripts] icloudpd = "icloudpd.cli:cli" entry point is not affected, because the setuptools wrapper does sys.exit(cli()).
Steps to reproduce
# pip install icloudpd==1.32.3 in a venv (linux x86_64), then force a connection error:
HTTPS_PROXY=http://127.0.0.1:9 icloudpd --password-provider parameter --no-progress-bar \
--cookie-directory /tmp/x --directory /tmp/x --username [email protected] --password x
# 2026-08-22 22:47:50 INFO Processing user: [email protected]
# 2026-08-22 22:47:50 INFO Cannot connect to Apple iCloud service
echo $?
# 0Same result invoking the bundled binary (site-packages/icloudpd/icloudpd) directly.
Real-world impact
I run icloudpd from a systemd timer with OnFailure= alerting. A full-library pass (21,250 assets) died after 12,822 with Cannot connect to Apple iCloud service; the unit recorded ExecMainStatus=0, no alert fired, and the next hourly incremental run reported success on top of a half-done library. Anyone using the exit status in cron/systemd/Docker health logic is affected the same way. (Note also that the connection error is logged at INFO level, which makes it even easier to miss.)
Suggested fix
import sys
from icloudpd.cli import cli
if __name__ == "__main__":
sys.exit(cli())(and possibly the same for src/starters/icloud.py if it has the same shape). Happy to open a PR if that's welcome.
Environment
- icloudpd 1.32.3 (
version:1.32.3, commit sha:2035bb1), PyPI wheelicloudpd-1.32.3-py2.py3-none-manylinux1_x86_64.manylinux_2_5_x86_64.whl - Debian 13 (LXC), Python 3.13.5 venv
Source: icloud-photos-downloader/icloud_photos_downloader