diff --git a/src/infrastructure/services/backend_consistency_service.rs b/src/infrastructure/services/backend_consistency_service.rs index 6a5738a3..0ee17d39 100644 --- a/src/infrastructure/services/backend_consistency_service.rs +++ b/src/infrastructure/services/backend_consistency_service.rs @@ -378,9 +378,11 @@ impl RecoverableJobHandler for BackendConsistencyCheck { }, }; if let Err(e) = backend.initialize().await { - return RunOutcome::Failed { - message: format!("probed backend init: {e}"), - }; + // Nothing has been scanned yet, so there is no cursor to keep + // — but the distinction still matters: an unreachable endpoint + // pauses and can be resumed once it is back, while a wrong + // bucket or bad credentials stays terminal. + return RunOutcome::from_domain_error(None, "probed backend init", &e); } if let Some(name) = &probed_storage { tracing::info!( diff --git a/src/infrastructure/services/backend_migration_service.rs b/src/infrastructure/services/backend_migration_service.rs index 286320b6..49722f29 100644 --- a/src/infrastructure/services/backend_migration_service.rs +++ b/src/infrastructure/services/backend_migration_service.rs @@ -452,9 +452,11 @@ impl RecoverableJobHandler for BackendMigrationService { // for the swap-hot-swap call in `finish_completed`. let target = build_entry_backend_typed(target_entry, &self.storage_path_fallback); if let Err(e) = target.initialize().await { - return RunOutcome::Failed { - message: format!("target backend init: {e}"), - }; + // Runs BEFORE `migration_readonly` is engaged, so pausing + // here holds no write freeze — an operator can leave it + // paused indefinitely and resume when the target comes back. + // A wrong bucket or bad credentials still fails terminally. + return RunOutcome::from_domain_error(None, "target backend init", &e); } // All guards passed. Engage server-wide read-only mode for diff --git a/src/infrastructure/services/s3_blob_backend.rs b/src/infrastructure/services/s3_blob_backend.rs index 03adb15d..8b13178c 100644 --- a/src/infrastructure/services/s3_blob_backend.rs +++ b/src/infrastructure/services/s3_blob_backend.rs @@ -105,10 +105,14 @@ impl BlobStorageBackend for S3BlobBackend { .send() .await .map_err(|e| { - DomainError::internal_error( - "S3", - format!("Cannot access bucket '{}': {}", self.bucket, e), - ) + // Classified like every other SDK call. A refused + // connection or a 5xx here is the endpoint being + // down, not the configuration being wrong, and the + // jobs that call `initialize()` should pause rather + // than fail on it. A genuine misconfiguration — + // wrong bucket, bad credentials — still lands as 4xx + // and stays terminal. + s3_domain_error("S3", format!("Cannot access bucket '{}'", self.bucket), &e) })?; tracing::info!("S3 blob backend initialized: bucket={}", self.bucket);