Video uploads are dropped forever when screen is locked while foreground upload is active
Nobody has claimed this yet.
- Dominant language
- Swift
- Stars
- 2.5k
- Forks
- 1k
- Avg merge
- 2d 18h
- Merged PRs (30d)
- 13
Description
How to use GitHub
- Please use the π reaction to show that you are affected by the same issue.
- Please don't comment if you have no relevant information to add. It's just extra noise for everyone subscribed to this issue.
- Subscribe to receive notifications on status change and new comments.
Bug report
Steps to reproduce
- while app in background, record a >100M video
- foreground app, let chunked transfer initiate
- lock screen
Expected behaviour
Video upload should continue, or resume when app is later foregrounded.
Actual behaviour
Video upload stops and is never tried again.
Logs
See below
Reasoning or why should it be changed/implemented?
Relieve user from frustration of never-uploaded-videos, don't lock them with Nextcloud app active in fear of auto lock screen or other hiccup finally leading to user being forced to manually upload their video to Nextcloud
Environment data
Code references are against upstream
master at 204f9e23b1 (2026-08-23). The "before" log lines are upstream's own logging at INFO level;
the "after" lines come from a later build with additional diagnostic logging.
Device: iPhone 12 mini, iOS 26.6.1, auto-upload on, Wi-Fi at home.
Analysis
Summary
Files large enough to need chunked upload (in practice, videos) are uploaded by a foreground Task
driving a plain Alamofire session β not by the background URLSession used for photos. On every
UIApplication.didEnterBackgroundNotification, NCNetworkingProcess cancels that task. The
cancellation handler in uploadChunkFile deletes the video's queued metadata row outright. The
discovery bookmark (autoUploadSinceDate) has already advanced past the asset, so no later scan
re-offers it. Result: lock the screen, press Home, or answer a call while the "keep application active"
banner is showing, and the video is silently and permanently dropped from auto-upload. It remains in
the camera roll; it is never uploaded.
The app doesn't tell this apart from the user tapping the banner's own Cancel button β both call the
same cancelCurrentUpload() and land in the same delete path.
I argue that this can and should probably be changed for a better user experience
Apple's contract (where it applies)
Entering the background is not a user intention about the app's work; it is a scene state change UIKit
reports for any reason, in particular when the screen is locked while app is still foregrounded β
sceneDidEnterBackground(_:):
Tells the delegate that the scene is running in the background and is no longer onscreen. [β¦] Use
this method to reduce your scene's memory usage, free up any shared resources, and clean up your
scene's user interface.
Apple's documented mechanism for finishing work that a background transition would otherwise
interrupt is a background task assertion β
beginBackgroundTask(expirationHandler:):
Call this method when leaving a task unfinished might be detrimental to your app's user experience.
[β¦] Call this method as early as possible before starting your task, preferably before your app
actually enters the background.
Neither document describes cancelling in-flight work on the transition; the second describes the
opposite. Nothing in the contract distinguishes a lock-screen transition from a Home-button one, and
nothing tells the app why it went to the background β but the app itself knows whether its own Cancel
button was tapped, because that is a separate code path (below).
Where the code departs from it
Every background transition cancels the in-flight chunked upload β
iOSClient/Networking/NCNetworkingProcess.swift (master, lines 114β125):
NotificationCenter.default.addObserver(forName: UIApplication.didEnterBackgroundNotification, object: nil, queue: nil) { [weak self] _ in
guard let self else { return }
Task {
β¦
await self.stopTimer()
await self.cancelCurrentTaskOnBackground()
await self.cancelCurrentUpload()
}
}
The banner's Cancel button calls the identical method β same file, lines 497 and 550:
onButtonTap: {
Task {
await self.cancelCurrentUpload()
}
}
cancelCurrentUpload() (lines 216β221) cancels currentUploadTask and currentUploadRequest. From
here on the two origins are indistinguishable.
The cancellation handler deletes the row β
iOSClient/Networking/NCNetworking+Upload.swift (master, lines 174β176 and 438β448):
} catch is CancellationError {
backupError = NKError(errorCode: -5, errorDescription: "Transfers was cancelled.")
await uploadCancelFile(metadata: metadata, directoryChunks: directory)
func uploadCancelFile(metadata: tableMetadata, directoryChunks: String? = nil) async {
if let directoryChunks {
await NCManageDatabase.shared.deleteChunksAsync(account: metadata.account, ocId: metadata.ocId, directory: directoryChunks)
}
self.utilityFileSystem.removeFile(atPath: β¦)
await NCManageDatabase.shared.deleteMetadataAsync(id: metadata.ocIdTransfer)
}
No error status, no retry, no log line. Contrast the non-chunked path in the same file:
uploadError(withMetadata:) requeues an auto-upload as metadataStatusUploadError on
NSURLErrorCancelled and only discards manual uploads.
Why it is permanent β iOSClient/Networking/NCAutoUpload.swift (master, lines 168β171): the
discovery bookmark is written the moment the batch is queued, before anything is uploaded:
if let metadata = metadatas.last {
let date = metadata.creationDate as Date
await self.database.updateAccountPropertyAsync(\.autoUploadSinceDate, value: date, account: session.account)
}
Once the row is deleted, the asset's creationDate is behind the bookmark and no scan ever sees it
again.
Field evidence
The symptom β log-0909-1831.txt, 2026-09-09 evening
Tester's account (verbatim): "I ran the app with 2 videos and 12 photos in the camera roll. When I
foregrounded it, it soon started to upload a video (with the infamous popup that warns user to keep app
active). I backgrounded the app, then shut the phone off. When I resumed operations, photos were finally
uploaded and successfully deleted from the roll, but the videos stayed in the roll, and were never
uploaded. [β¦] the videos were never uploaded and will never be."
The log, INFO level, upstream logging only:
21:47:58 [DEBUG] Application will enter in foreground
21:48:00 [DEBUG] Automatic upload, new 2 assets found β 0243.mov (21:23:09), 0244.mov (21:29:28)
21:48:02 β¦ Network response request: β¦/Photos/2026/09/26-09-09 21-23-09 0243.mov, result: failure(β¦ 404) β not on server yet
21:48:03 β¦ Network response request: β¦/dav/uploads/<redacted>/F282F2BD-β¦, result: failure(β¦ 404) β chunk folder PROPFIND
21:48:03 β¦ Network response request: β¦/dav/uploads/<redacted>/F282F2BD-β¦, result: failure(β¦ inputDataNilOrZeroLength) β MKCOL 201, empty body
21:48:03 [DEBUG] Scene will resign active
21:48:04 [DEBUG] Application did enter in background β 1 s into the chunked upload
21:48:21 [DEBUG] Application will enter in foreground
21:48:22 [DEBUG] Automatic upload, new 12 assets found β the photos; 0243 is NOT re-found
21:48:26 β¦ Network response request: β¦/Photos/2026/09/26-09-09 21-29-28 0244.mov, result: failure(β¦ 404)
21:48:26 β¦ Network response request: β¦/dav/uploads/<redacted>/72238F00-β¦, result: failure(β¦ 404)
21:48:26 β¦ Network response request: β¦/dav/uploads/<redacted>/72238F00-β¦, result: failure(β¦ inputDataNilOrZeroLength)
21:48:28 [DEBUG] Application did enter in background β 2 s into the second chunked upload
Neither 26-09-09 21-23-09 0243.mov nor 26-09-09 21-29-28 0244.mov appears again in this log or in
any log recorded since (checked across every file through 2026-09-16). No [SUCCESS] Uploaded file:,
no error, no retry. The twelve photos, which go through the background URLSession, uploaded normally
afterwards and were removed from the camera roll; the two videos stayed.
The second video is also the proof that the bookmark had moved: the 21:48:22 discovery pass found the
12 new photos but not 0243, whose row had just been deleted.
The control β log-0909-2155.txt, same evening, app kept in the foreground
Tester's account: "I've just created a 1GB video, opened our current version of the app, got the
chunked upload popup, waited until it finished, and it indeed finished properly."
22:28:14 [DEBUG] Application will enter in foreground
22:28:15 [DEBUG] Automatic upload, new 1 assets found
22:30:38 [SUCCESS] Uploaded file: β¦/Photos/2026/09/26-09-09 22-20-22 0257.mov
Same code, same kind of file, only difference: no background transition. Chunked upload works; it is
the transition that kills it.
With cancellation treated as retryable for auto-uploads β log-j.txt, 2026-09-16
11:03:21 [BGSYNC] Deferring 26-09-16 10-57-21 0896.mov β¦: needs chunked upload, which requires the app in the foreground
β¦
12:22:07 [DEBUG] Application will enter in foreground β chunked upload of 0896.mov starts
12:22:12 [DEBUG] Application did enter in background β tester backgrounds the app mid-transfer
12:22:13 [ERROR] Upload file: β¦/Photos/2026/09/26-09-16 10-57-21 0896.mov, result: error -5 β cancelled
12:27:19 [SUCCESS] Uploaded file: β¦/Photos/2026/09/26-09-16 10-57-21 0896.mov β retried by the queue timer, 5 min later
12:27:24 [NETWORKING TASKS] Removed 1 uploaded asset(s) from camera roll
Identical trigger to 2026-09-09; the row survived, the retry succeeded, the asset was cleaned up.
Suggestion
Backgrounding and the Cancel button reach cancelCurrentUpload() from two different call sites, so
the app already has the information it throws away: whether the cancellation was a user decision. The
two cases deserve different outcomes.
- User taps Cancel on the banner β a knowing decision about this file. Whatever the product
semantics of that button are meant to be (see the note below), the current "discard" behaviour is at
least defensible here. - The scene entered the background β screen lock, Home, app switcher, an incoming call, a
notification tap, Face ID timeout. None of these say anything about the file. The upload should be
deferred and retried: keep the metadata row (metadataStatusUploadErroror equivalent), keep the
chunks already written so the retry can resume rather than restart, and let the normal queue
processing pick it up on the next foreground session. This is exactly what the non-chunked path
already does onNSURLErrorCancelledfor auto-uploads.
Two smaller observations that follow from the same distinction:
- The
didEnterBackgroundNotificationobserver cancels immediately, at the start of the transition.
Apple'sbeginBackgroundTaskgives a short window precisely so that "leaving a task unfinished" can
be avoided; a chunk that is mid-PUT could be allowed to finish before the task is torn down, so the
retry has less to redo. - The banner's single Cancel button is ambiguous for auto-uploaded files: a user who taps it may
mean "not now" or "never this file". If the background case becomes "retry later", the button is the
only remaining way to say "never", and its label should say so β or it should offer both.
Test conditions
2026-09-09 (symptom): two videos (21:23, 21:29) and a burst of twelve photos in the camera roll;
app foregrounded at 21:47:58; app backgrounded 1β2 s after each chunked upload began (21:48:04,
21:48:28); phone then powered off; on resume, photos uploaded and were removed from the roll, videos
remained and never uploaded.
2026-09-09 (control): one 1 GB video; app foregrounded at 22:28:14 and kept in the foreground for the
whole transfer; upload completed at 22:30:38.
2026-09-16 (after modification): one video deferred from a background pass at 11:03; app foregrounded at 12:22:07,
backgrounded at 12:22:12 mid-transfer; upload retried and completed at 12:27:19.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up β it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in iOSClient/Networking/NCNetworkingProcess.swift, tracing the didEnterBackgroundNotification handler, cancelCurrentUpload(), and the banner Cancel action. Then read iOSClient/Networking/NCNetworking+Upload.swift and NCAutoUpload.swift, comparing chunked cancellation with uploadError(withMetadata:). Reproduce with a large video and a background transition; done means background cancellation preserves the auto-upload for a later retry without changing explicit Cancel behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- ios, swift
- Domain
- mobile, networking
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 58/100