From b07359cdb5c2b34b62ae00ed6a7050196d6fe97b Mon Sep 17 00:00:00 2001 From: BoulderBadgeDad Date: Sun, 28 Jun 2026 15:32:47 -0700 Subject: [PATCH] downloads: make the "file not found" failure actionable instead of opaque MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Discord (Shdjfgatdif, standalone): some downloads complete on disk but get marked failed with "File not found on disk after 5 search attempts. Expected: " — which tells the user nothing about where we looked or what to check. This is deliberately a DIAGNOSTIC fix, not a behavior change. The finder + path handling are sound (verified: docker_resolve_path no-ops in standalone, the finder walks the configured soulseek.download_path and resolves a present file). When it still misses after slskd reported the transfer Succeeded, the cause is environmental — either the file is still landing (timing) or, the classic standalone gotcha, SoulSync's download_path doesn't point at slskd's actual download dir. Neither is something our code can "fix"; the user fixes the config, or the file arrives. So: name the folder we actually searched and spell out the two real causes, turning an opaque failure into self-diagnosis ("oh, my download folder's wrong"). Retry/wait behavior is left untouched on purpose — widening the window does nothing for a path mismatch and I can't justify it for this user. Also normalizes the slskd backslash path so the reported filename is the leaf, not the whole "@@@user\folder\file" string. Updated the existing not-found test to pin the new actionable message (searched path + config hint + filename). 588 downloads tests green, ruff clean. --- core/downloads/post_processing.py | 14 +++++++++++++- tests/downloads/test_downloads_post_processing.py | 7 ++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/core/downloads/post_processing.py b/core/downloads/post_processing.py index 5dbbae97..2be79eba 100644 --- a/core/downloads/post_processing.py +++ b/core/downloads/post_processing.py @@ -453,7 +453,19 @@ def run_post_processing_worker(task_id: str, batch_id: str, deps: PostProcessDep logger.error(f"[Post-Processing] Task {task_id} was completed by stream processor - not marking as failed") return download_tasks[task_id]['status'] = 'failed' - download_tasks[task_id]['error_message'] = f'File not found on disk after {_file_search_max_retries} search attempts. Expected: {os.path.basename(task_filename)}' + # slskd reported the transfer complete, but the finder never located + # the file under the configured download folder. Name the folder we + # searched and the two real causes — "still being written" (timing) + # or "SoulSync's download path doesn't match slskd's" (the classic + # standalone config mismatch) — so the user can self-diagnose instead + # of getting an opaque "not found". (Discord: Shdjfgatdif.) + _searched_name = os.path.basename((task_filename or '').replace('\\', '/')) or task_filename + download_tasks[task_id]['error_message'] = ( + f"slskd reported '{_searched_name}' downloaded, but it never appeared " + f"under the download folder ({download_dir}) after {_file_search_max_retries} " + f"checks. Either it's still being written, or SoulSync's download path " + f"doesn't match slskd's download directory — they must point at the same folder." + ) deps.on_download_completed(batch_id, task_id, False) return diff --git a/tests/downloads/test_downloads_post_processing.py b/tests/downloads/test_downloads_post_processing.py index 701c6e2b..c8ca26b4 100644 --- a/tests/downloads/test_downloads_post_processing.py +++ b/tests/downloads/test_downloads_post_processing.py @@ -170,7 +170,12 @@ def test_file_not_found_after_retries_marks_failed(monkeypatch): deps, rec = _build_deps() pp.run_post_processing_worker('t1', 'b1', deps) assert download_tasks['t1']['status'] == 'failed' - assert 'File not found on disk' in download_tasks['t1']['error_message'] + # Actionable failure: names the folder searched + the two real causes, so a + # standalone user with a path mismatch can self-diagnose (Discord: Shdjfgatdif). + msg = download_tasks['t1']['error_message'] + assert './downloads' in msg # the folder we actually searched + assert "download path doesn't match slskd" in msg # the config-mismatch hint + assert 'song.flac' in msg # the file slskd reported assert ('on_complete', ('b1', 't1', False), {}) in rec.calls