From cb0f9b9590d419df8ed8f669b42b64bc3c7c2f8f Mon Sep 17 00:00:00 2001 From: Tom Hubrecht Date: Mon, 24 Nov 2025 17:27:12 +0100 Subject: [PATCH] libstore/transfer: Warn by default instead of throw for destination changes Fixes #1004 Change-Id: I2d583fbad40b3c95e37b70167a81c8fb492d5668 --- .../rl-next/final-destination-warning.md | 10 +++++ lix/libstore/filetransfer.cc | 41 +++++++++++++------ 2 files changed, 38 insertions(+), 13 deletions(-) create mode 100644 doc/manual/rl-next/final-destination-warning.md diff --git a/doc/manual/rl-next/final-destination-warning.md b/doc/manual/rl-next/final-destination-warning.md new file mode 100644 index 000000000..1c2698d05 --- /dev/null +++ b/doc/manual/rl-next/final-destination-warning.md @@ -0,0 +1,10 @@ +--- +synopsis: "Warn instead of erroring when the final destination of a transfer changes in-flight" +cls: [4641] +issues: [fj#1004] +category: "Miscellany" +credits: [thubrecht] +--- + +Lix will now emit a warning during downloads where the final destination changes suddently mid-transfer instead of throwing an error. +This transfer behavior has been known to happen very rarely while fetching from some CDNs. diff --git a/lix/libstore/filetransfer.cc b/lix/libstore/filetransfer.cc index 185b5b3a5..3a1374b29 100644 --- a/lix/libstore/filetransfer.cc +++ b/lix/libstore/filetransfer.cc @@ -1093,15 +1093,6 @@ struct curlFileTransfer : public FileTransfer co_return result::current_exception(); } - void throwChangedTarget(std::string_view what, std::string_view from, std::string_view to) - { - if (!from.empty() && from != to) { - throw FileTransferError( - Misc, {}, "uri %s changed %s from %s to %s during transfer", uri, what, from, to - ); - } - } - kj::Promise> prepareRetry( const std::string & context, const std::chrono::milliseconds & waitTime, @@ -1135,17 +1126,41 @@ struct curlFileTransfer : public FileTransfer kj::Promise> restartTransfer(const std::chrono::milliseconds & timeout) try { + auto onChange = + [&](std::string_view what, std::string_view from, std::string_view to, bool throw_ + ) -> void { + if (!from.empty() && from != to) { + FileTransferError e = FileTransferError( + Misc, + {}, + "uri %s changed %s from %s to %s during transfer", + uri, + what, + from, + to + ); + + if (throw_) { + throw e; + } + + logWarning(e.info()); + } + }; + // use the effective URI of the previous transfer for retries. this avoids // some silent corruption if a redirect changes between starting and retry const auto & uri = metadata.effectiveUri.empty() ? this->uri : metadata.effectiveUri; auto newMeta = TRY_AWAIT(startTransfer(uri, timeout, totalReceived)); - throwChangedTarget("final destination", metadata.effectiveUri, newMeta.effectiveUri); - throwChangedTarget("ETag", metadata.etag, newMeta.etag); - throwChangedTarget( + + onChange("final destination", metadata.effectiveUri, newMeta.effectiveUri, false); + onChange("ETag", metadata.etag, newMeta.etag, true); + onChange( "immutable url", metadata.immutableUrl.value_or(""), - newMeta.immutableUrl.value_or("") + newMeta.immutableUrl.value_or(""), + true ); co_return result::success(); } catch (...) {