diff --git a/desktop/crates/cloudreve-api/src/error.rs b/desktop/crates/cloudreve-api/src/error.rs index 63c4b81e..bcd9d3fd 100644 --- a/desktop/crates/cloudreve-api/src/error.rs +++ b/desktop/crates/cloudreve-api/src/error.rs @@ -46,6 +46,7 @@ pub enum ErrorCode { IncorrectPassword = 40069, LockConflict = 40073, StaleVersion = 40076, + EntityNotExist = 40077, BatchOperationNotFullyCompleted = 40081, DomainNotLicensed = 40087, AnonymousAccessDenied = 40088, @@ -65,6 +66,7 @@ impl ErrorCode { 40069 => Some(Self::IncorrectPassword), 40073 => Some(Self::LockConflict), 40076 => Some(Self::StaleVersion), + 40077 => Some(Self::EntityNotExist), 40081 => Some(Self::BatchOperationNotFullyCompleted), 40087 => Some(Self::DomainNotLicensed), 40088 => Some(Self::AnonymousAccessDenied), @@ -198,6 +200,11 @@ impl ApiError { matches!(self, ApiError::AccessTokenExpired) } + /// Check if this error reports a missing entity (code 40077) + pub fn is_entity_not_exist(&self) -> bool { + matches!(self, ApiError::ApiError { code, .. } if *code == 40077) + } + /// Check if this error requires login pub fn requires_login(&self) -> bool { matches!( diff --git a/desktop/crates/cloudreve-sync/src/drive/commands.rs b/desktop/crates/cloudreve-sync/src/drive/commands.rs index 4bf1db03..cf9601ac 100644 --- a/desktop/crates/cloudreve-sync/src/drive/commands.rs +++ b/desktop/crates/cloudreve-sync/src/drive/commands.rs @@ -239,11 +239,21 @@ impl Mount { request.entity = Some(meta.etag.clone()); } } - let entity_url_res = self - .cr_client - .get_file_url(&request) - .await - .context("failed to get file url")?; + let entity_url_res = match self.cr_client.get_file_url(&request).await { + Err(e) if e.is_entity_not_exist() && request.entity.is_some() => { + // The entity id cached in local inventory can go stale when the + // remote file is re-uploaded or migrated; retry letting the + // server pick the primary entity. + tracing::info!(target: "drive::commands", path = %path.display(), "Preferred entity no longer exists, retrying without it"); + let mut retry = request.clone(); + retry.entity = None; + self.cr_client + .get_file_url(&retry) + .await + .context("failed to get file url")? + } + res => res.context("failed to get file url")?, + }; // Get the download URL from the response let download_url = entity_url_res diff --git a/desktop/crates/cloudreve-sync/src/tasks/download.rs b/desktop/crates/cloudreve-sync/src/tasks/download.rs index 6d1e6a24..3160cbfa 100644 --- a/desktop/crates/cloudreve-sync/src/tasks/download.rs +++ b/desktop/crates/cloudreve-sync/src/tasks/download.rs @@ -338,11 +338,19 @@ impl<'a> DownloadTask<'a> { .clone(); } - let entity_url_res = self - .cr_client - .get_file_url(&request) - .await - .context("failed to get file url")?; + let entity_url_res = match self.cr_client.get_file_url(&request).await { + Err(e) if e.is_entity_not_exist() && request.entity.is_some() => { + // Entity id cached from a previous file info response may be + // stale; retry letting the server pick the primary entity. + let mut retry = request.clone(); + retry.entity = None; + self.cr_client + .get_file_url(&retry) + .await + .context("failed to get file url")? + } + res => res.context("failed to get file url")?, + }; let download_url = entity_url_res .urls