Fix library maintenance path fixes failing silently (#207)

fix_finding() was using a potentially stale transfer_folder that was
only refreshed during scheduled job runs, not on manual fix attempts.
Now re-reads the transfer path from config before each fix, matching
the same logic used by _run_next_job().

Also surfaces fix failure reasons to the user — bulk fix now logs each
failure with finding ID and error, and the frontend toast shows the
actual error message instead of just "X failed".
This commit is contained in:
Broque Thomas 2026-03-29 16:25:36 -07:00
parent 20452859c5
commit 98463e5d43
4 changed files with 32 additions and 6 deletions

View file

@ -757,6 +757,11 @@ class RepairWorker:
finding_id: ID of the finding to fix
fix_action: Optional action override (e.g. 'staging' or 'delete' for orphan files)
"""
# Refresh transfer folder from config before each fix — same logic as _run_next_job
if self._config_manager:
raw = self._config_manager.get('soulseek.transfer_path', './Transfer')
self.transfer_folder = self._resolve_path(raw)
conn = None
try:
conn = self.db._get_connection()
@ -2301,14 +2306,18 @@ class RepairWorker:
fixed = 0
failed = 0
errors = []
for fid in ids_to_fix:
result = self.fix_finding(fid, fix_action=fix_action)
if result.get('success'):
fixed += 1
else:
error_msg = result.get('error', 'unknown error')
logger.warning("Fix failed for finding #%s: %s", fid, error_msg)
errors.append({'id': fid, 'error': error_msg})
failed += 1
return {'fixed': fixed, 'failed': failed, 'total': len(ids_to_fix)}
return {'fixed': fixed, 'failed': failed, 'total': len(ids_to_fix), 'errors': errors}
except Exception as e:
logger.error("Error bulk fixing findings: %s", e, exc_info=True)
return {'fixed': 0, 'failed': 0, 'total': 0, 'error': str(e)}

View file

@ -18999,6 +18999,15 @@ def get_version_info():
"title": "What's New in SoulSync",
"subtitle": f"Version {SOULSYNC_VERSION} — Latest Changes",
"sections": [
{
"title": "🔧 Fix Library Maintenance Path Fixes Failing Silently",
"description": "Path mismatch fixes now use fresh config and report errors to the UI",
"features": [
"• Transfer folder path is re-read from config before each fix attempt",
"• Fix failure reasons are now shown in the toast notification",
"• Bulk fix failures are logged individually with finding ID and error details"
]
},
{
"title": "🔧 Fix Spotify Manual Match Storing Wrong IDs",
"description": "Manual match modals no longer store iTunes/Deezer IDs in Spotify ID columns",
@ -44034,7 +44043,8 @@ def repair_findings_bulk_fix():
'success': True,
'fixed': result.get('fixed', 0),
'failed': result.get('failed', 0),
'total': result.get('total', 0)
'total': result.get('total', 0),
'errors': result.get('errors', [])
}), 200
except Exception as e:
logger.error(f"Error bulk fixing findings: {e}")

View file

@ -3403,6 +3403,7 @@ function closeHelperSearch() {
const WHATS_NEW = {
'2.1': [
// Newest features first
{ title: 'Fix Path Mismatch Fixes', desc: 'Library Maintenance path fixes now use fresh config and show error reasons in the toast' },
{ title: 'Fix Wrong Spotify IDs', desc: 'Manual match no longer stores iTunes IDs as Spotify IDs — detects actual provider from results' },
{ title: 'Spotify Enrichment Budget', desc: 'Background enrichment worker caps at 3,000 items/day to prevent rate limit bans — resets at midnight' },
{ title: 'Per-Artist Library Sync', desc: 'Validate files and clean stale entries per artist from the enhanced library view', page: 'library', selector: '.library-controls' },

View file

@ -55821,7 +55821,11 @@ async function fixAllMatchingFindings() {
});
const result = await response.json();
if (result.success) {
showToast(`Fixed ${result.fixed}${result.failed ? `, ${result.failed} failed` : ''} of ${result.total}`, result.fixed > 0 ? 'success' : 'error');
let msg = `Fixed ${result.fixed}${result.failed ? `, ${result.failed} failed` : ''} of ${result.total}`;
if (result.errors && result.errors.length > 0) {
msg += `: ${result.errors[0].error}`;
}
showToast(msg, result.fixed > 0 ? 'success' : 'error');
} else {
showToast(result.error || 'Bulk fix failed', 'error');
}
@ -56013,7 +56017,7 @@ async function bulkFixFindings() {
if (!orphanFixAction) return;
}
let fixed = 0, failed = 0;
let fixed = 0, failed = 0, lastError = '';
showToast(`Fixing ${ids.length} findings...`, 'info');
for (const id of ids) {
@ -56030,14 +56034,16 @@ async function bulkFixFindings() {
});
const result = await response.json();
if (result.success) fixed++;
else failed++;
else { failed++; lastError = result.error || 'unknown error'; }
} catch {
failed++;
}
}
_repairSelectedFindings.clear();
showToast(`Fixed ${fixed}${failed ? `, ${failed} failed` : ''}`, fixed > 0 ? 'success' : 'error');
let fixMsg = `Fixed ${fixed}${failed ? `, ${failed} failed` : ''}`;
if (failed && lastError) fixMsg += `: ${lastError}`;
showToast(fixMsg, fixed > 0 ? 'success' : 'error');
loadRepairFindingsDashboard();
loadRepairFindings();
updateRepairStatus();