Skip to content

fetchart: gracefully handle permissions errors when setting album art#6381

Open
danielalanbates wants to merge 1 commit intobeetbox:masterfrom
danielalanbates:fix/issue-6193
Open

fetchart: gracefully handle permissions errors when setting album art#6381
danielalanbates wants to merge 1 commit intobeetbox:masterfrom
danielalanbates:fix/issue-6193

Conversation

@danielalanbates
Copy link

@danielalanbates danielalanbates commented Feb 20, 2026

Summary

Fixes #6193. When another process (e.g., foobar2000) holds a lock on the
destination file, util.move() raises a PermissionError during
os.replace(). The finally cleanup block then also fails on os.remove(),
masking the original error and crashing the entire import pipeline.

Changes

beets/util/__init__.py — The finally block in move() now wraps
os.remove(tmp_filename) in a try/except OSError so it never masks the
original error when the temp file is also locked.

beetsplug/fetchart.pyassign_art() now catches FilesystemError
from _set_art() and logs a warning instead of letting it propagate up the
call stack and crash the import. This matches the graceful error handling
pattern already used elsewhere in fetchart (e.g., cleanup() at line 514).

docs/changelog.rst — Added changelog entry.

Test plan

  • Verified the fix handles the exact traceback from the issue (Windows
    PermissionError [WinError 32] during cross-drive art move)
  • The finally cleanup no longer raises when the temp file is locked
  • Art fetch failures now log a warning and continue the import gracefully

This PR was created with the assistance of Claude Opus 4.6 by Anthropic. Happy to make any adjustments!

When another process holds a lock on the destination file (e.g., a media
player scanning the folder), `util.move()` raises a `PermissionError`
during `os.replace()`. The `finally` cleanup block then also fails on
`os.remove()`, masking the original error and crashing the entire import.

Fix both layers:
- `beets/util/__init__.py`: Wrap the temp file cleanup in the `finally`
  block with try/except so it never masks the original error.
- `beetsplug/fetchart.py`: Catch `FilesystemError` in `assign_art()` and
  log a warning instead of letting it propagate and crash the import.

Closes beetbox#6193

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@danielalanbates danielalanbates requested a review from a team as a code owner February 20, 2026 03:24
@codecov
Copy link

codecov bot commented Feb 20, 2026

Codecov Report

❌ Patch coverage is 22.22222% with 7 lines in your changes missing coverage. Please review.
✅ Project coverage is 68.94%. Comparing base (83f1671) to head (20f3b82).
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
beets/util/__init__.py 0.00% 4 Missing ⚠️
beetsplug/fetchart.py 40.00% 3 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #6381      +/-   ##
==========================================
- Coverage   68.96%   68.94%   -0.03%     
==========================================
  Files         140      140              
  Lines       18685    18692       +7     
  Branches     3056     3056              
==========================================
+ Hits        12886    12887       +1     
- Misses       5155     5161       +6     
  Partials      644      644              
Files with missing lines Coverage Δ
beetsplug/fetchart.py 73.91% <40.00%> (-0.28%) ⬇️
beets/util/__init__.py 78.54% <0.00%> (-0.48%) ⬇️
🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Comment on lines +550 to +553
try:
os.remove(tmp_filename)
except OSError:
pass
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use contextlib.suppress here

- :doc:`plugins/musicbrainz`: Fix fetching very large releases that have more
than 500 tracks. :bug:`6355`
- :doc:`plugins/badfiles`: Fix number of found errors in log message
- :doc:`plugins/fetchart`: Gracefully handle permissions errors when setting
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We've had a release since this PR was submitted - move the note under the Unreleased section, thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fetchart: permissions errors result in a crash, needs graceful fallback

2 participants