Skip to content

Commit 656eccb

Browse files
ianbmacdonaldclaude
andcommitted
fix: fail fast on insufficient disk space instead of retry loop
When a download fails with CURLE_WRITE_ERROR (code 23) due to disk full, the retry logic would delete the partial file and restart from zero — repeating indefinitely and wasting bandwidth. This adds: - Pre-flight disk space check before downloading, comparing available space against total download size - Detection of CURLE_WRITE_ERROR + low disk as a fatal (non-retryable) condition with a clear error message - disk_full flag on DownloadResult to short-circuit the retry loop Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent a2e6a8a commit 656eccb

File tree

3 files changed

+46
-1
lines changed

3 files changed

+46
-1
lines changed

src/cpp/include/lemon/utils/http_client.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ struct DownloadResult {
3737
size_t bytes_downloaded = 0; // Bytes downloaded in this attempt
3838
size_t total_bytes = 0; // Total file size (if known)
3939
bool can_resume = false; // Whether partial download can be resumed
40+
bool disk_full = false; // True if download failed due to insufficient disk space
4041
};
4142

4243
// Progress callback returns bool: true = continue, false = cancel download

src/cpp/server/model_manager.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1805,6 +1805,21 @@ void ModelManager::download_from_manifest(const json& manifest, std::map<std::st
18051805
total_download_size += file_desc["size"].get<size_t>();
18061806
}
18071807

1808+
// Pre-flight disk space check: fail fast before downloading anything
1809+
{
1810+
std::error_code ec;
1811+
auto si = fs::space(fs::path(download_path), ec);
1812+
if (!ec && total_download_size > si.available) {
1813+
std::ostringstream oss;
1814+
oss << "Insufficient disk space: download requires "
1815+
<< std::fixed << std::setprecision(1)
1816+
<< (total_download_size / (1024.0 * 1024.0 * 1024.0)) << " GB but only "
1817+
<< (si.available / (1024.0 * 1024.0 * 1024.0)) << " GB is available on "
1818+
<< download_path;
1819+
throw std::runtime_error(oss.str());
1820+
}
1821+
}
1822+
18081823
for (const auto& file_desc : manifest["files"]) {
18091824
file_index++;
18101825
std::string filename = file_desc["name"].get<std::string>();

src/cpp/server/utils/http_client.cpp

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,7 @@ DownloadResult HttpClient::download_attempt(const std::string& url,
398398

399399
if (res != CURLE_OK) {
400400
bool retryable = false;
401+
bool disk_full = false;
401402
switch (res) {
402403
case CURLE_COULDNT_CONNECT:
403404
case CURLE_COULDNT_RESOLVE_HOST:
@@ -410,6 +411,17 @@ DownloadResult HttpClient::download_attempt(const std::string& url,
410411
case CURLE_SSL_CONNECT_ERROR:
411412
retryable = true;
412413
break;
414+
case CURLE_WRITE_ERROR: {
415+
// CURLE_WRITE_ERROR (23) typically means disk full.
416+
// Check available disk space to confirm.
417+
std::error_code ec;
418+
auto si = fs::space(output_path_fs.parent_path(), ec);
419+
if (!ec && si.available < 1024 * 1024) { // Less than 1 MB free
420+
disk_full = true;
421+
}
422+
retryable = false;
423+
break;
424+
}
413425
default:
414426
retryable = false;
415427
}
@@ -419,9 +431,20 @@ DownloadResult HttpClient::download_attempt(const std::string& url,
419431
current_file_size = fs::file_size(output_path_fs);
420432
}
421433
result.can_resume = retryable && (current_file_size > 0);
434+
result.disk_full = disk_full;
422435

423436
std::ostringstream oss;
424-
oss << "Download failed: " << result.curl_error << " (CURL code: " << result.curl_code << ")";
437+
if (disk_full) {
438+
oss << "Disk full: not enough space to complete download";
439+
std::error_code ec;
440+
auto si = fs::space(output_path_fs.parent_path(), ec);
441+
if (!ec) {
442+
oss << " (" << std::fixed << std::setprecision(1)
443+
<< (si.available / (1024.0 * 1024.0)) << " MB free)";
444+
}
445+
} else {
446+
oss << "Download failed: " << result.curl_error << " (CURL code: " << result.curl_code << ")";
447+
}
425448
if (result.bytes_downloaded > 0) {
426449
oss << "\n Downloaded " << (result.bytes_downloaded / (1024.0 * 1024.0)) << " MB before failure";
427450
}
@@ -582,6 +605,12 @@ DownloadResult HttpClient::download_file(const std::string& url,
582605
return final_result;
583606
}
584607

608+
// If disk is full, fail immediately — retrying will just waste bandwidth
609+
if (final_result.disk_full) {
610+
LOG(ERROR, "HttpClient") << "[Download] " << final_result.error_message << std::endl;
611+
return final_result;
612+
}
613+
585614
if (final_result.success) {
586615
// Download complete - rename .partial to final path
587616
std::error_code ec;

0 commit comments

Comments
 (0)