Skip to content

Instantly share code, notes, and snippets.

@tomholford
Created March 1, 2026 19:45
Show Gist options
  • Select an option

  • Save tomholford/a5a043648a16974753e973b167785670 to your computer and use it in GitHub Desktop.

Select an option

Save tomholford/a5a043648a16974753e973b167785670 to your computer and use it in GitHub Desktop.
Fixing rclone Proton Drive uploads — block verification, retry, draft cleanup (March 2026)

Fixing rclone Proton Drive Uploads (March 2026)

Proton Drive uploads via rclone have been broken since ~November 2025. This documents the root causes, fixes, and testing.

Problem

Three issues combine to break uploads:

  1. Missing block verification tokens — Proton's storage backend now requires a Verifier.Token per block in POST /drive/blocks. Without it, every block upload fails with 422 / Code=200501. This is the primary cause.
  2. Broken shouldRetry()backend/protondrive/protondrive.go:248 always returns (false, err), disabling rclone's pacer retry logic entirely.
  3. Orphaned draft accumulation — Failed uploads leave draft links on Proton's servers. Subsequent uploads hit Code=2500 ("already exists") and GetRevisions returns Code=2501 ("not found") for the broken draft, causing an unrecoverable error loop.

Solution

Changes across three repos in the rclone ecosystem:

go-proton-api (fix/block-verification branch)

  • Added Verifier struct and VerificationRes struct to block_types.go
  • Added Verifier field to BlockUploadInfo
  • Added Verification() API endpoint to block.goGET /drive/shares/{shareID}/links/{linkID}/revisions/{revisionID}/verification

Proton-API-Bridge (fix/upload-retry branch)

  • Block verification (uploadAndCollectBlockData): fetches VerificationCode + ContentKeyPacket from the verification endpoint, derives a verifier session key, and generates per-block verification tokens via XOR of the verification code with leading bytes of each block's ciphertext (algorithm sourced from ProtonDriveApps/sdk)
  • Block-level retry (uploadBlockWrapper): 5 attempts with exponential backoff (2s→32s) for transient errors (Code=200501, Status=429, 5xx)
  • Orphaned draft cleanup (handleRevisionConflict): when GetRevisions fails with 2501 and link is in draft state, deletes the orphaned draft and signals retry

rclone (fix/protondrive-backups branch)

Cherry-picked from existing PRs/commits:

  • shouldRetry fix (from PR #9080): properly retries Code=200501, Status=429, 5xx errors
  • shouldRetry tests: 10 table-driven test cases
  • Crash fix (from issue #9117): o.Size() instead of *o.originalSize nil deref in Open()

Testing

  1. Cross-compiled static linux/amd64 binary (CGO_ENABLED=0)
  2. Injected into running gitea-backup container on VPS
  3. Small file upload — succeeded
  4. Full 1.39 GiB Gitea backup upload — succeeded in 5m32s, SHA1 verified
  5. Rebuilt Docker image with patched binary, deployed via docker compose
  6. Next cron backup scheduled for Saturday 2026-03-07 15:00 UTC

Related PRs & Issues

Repo PR/Issue Status Description
rclone/rclone #9080 Open shouldRetry fix
rclone/rclone #9081 Open Block verification + draft cleanup (rclone-level)
rclone/rclone #9117 Open nil originalSize crash
rclone/Proton-API-Bridge #1 Open Draft cleanup + verification (Bridge-level)
rclone/go-proton-api #2 Open Verification endpoint + Verifier types
henrybear327/Proton-API-Bridge #34 Open Verification API (LouisBrunner)
henrybear327/go-proton-api #5 Open Verification endpoint (LouisBrunner)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment