-
Notifications
You must be signed in to change notification settings - Fork 2k
Improve error message when database directory is not writable #6294
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -805,7 +805,7 @@ def parse_subcommand(self, args): | |||||||||||||||||
|
|
||||||||||||||||||
|
|
||||||||||||||||||
| def _setup( | ||||||||||||||||||
| options: optparse.Values, | ||||||||||||||||||
| options: optparse.Values, lib: library.Library | None | ||||||||||||||||||
| ) -> tuple[list[Subcommand], library.Library]: | ||||||||||||||||||
| """Prepare and global state and updates it with command line options. | ||||||||||||||||||
|
|
||||||||||||||||||
|
|
@@ -821,8 +821,9 @@ def _setup( | |||||||||||||||||
| subcommands = list(default_commands) | ||||||||||||||||||
| subcommands.extend(plugins.commands()) | ||||||||||||||||||
|
|
||||||||||||||||||
| lib = _open_library(config) | ||||||||||||||||||
| plugins.send("library_opened", lib=lib) | ||||||||||||||||||
| if lib is None: | ||||||||||||||||||
| lib = _open_library(config) | ||||||||||||||||||
| plugins.send("library_opened", lib=lib) | ||||||||||||||||||
|
|
||||||||||||||||||
| return subcommands, lib | ||||||||||||||||||
|
|
||||||||||||||||||
|
|
@@ -890,9 +891,25 @@ def _open_library(config: confuse.LazyConfig) -> library.Library: | |||||||||||||||||
| lib.get_item(0) # Test database connection. | ||||||||||||||||||
| except (sqlite3.OperationalError, sqlite3.DatabaseError) as db_error: | ||||||||||||||||||
| log.debug("{}", traceback.format_exc()) | ||||||||||||||||||
| # Check for permission-related errors and provide a helpful message | ||||||||||||||||||
| error_str = str(db_error).lower() | ||||||||||||||||||
| dbpath_display = util.displayable_path(dbpath) | ||||||||||||||||||
| if "unable to open" in error_str: | ||||||||||||||||||
|
||||||||||||||||||
| if "unable to open" in error_str: | |
| permission_error_strings = ( | |
| "unable to open", | |
| "readonly", | |
| "read-only", | |
| "attempt to write a readonly database", | |
| ) | |
| if any(msg in error_str for msg in permission_error_strings): |
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -12,155 +12,6 @@ Unreleased | |||||||||||
| New features | ||||||||||||
| ~~~~~~~~~~~~ | ||||||||||||
|
|
||||||||||||
| - :doc:`plugins/smartplaylist`: The ``splupdate`` command output is | ||||||||||||
| restructured. The per-playlist summary now includes a track count. Per-track | ||||||||||||
| details are shown only when ``-v`` flag is provided (``beet -v splupdate``). | ||||||||||||
| The ``--pretend`` flag produces the same output but reports *"N playlists | ||||||||||||
| would be updated"* instead of *"N playlists updated"*. The ``--format`` option | ||||||||||||
| allows customizing the track line format. The ``--pretend-paths`` option was | ||||||||||||
| removed (use ``--format='$path'`` instead). :bug:`6183` | ||||||||||||
| - :ref:`import-cmd`: When importing an archive (zip, tar, rar, or 7z) with | ||||||||||||
| ``move: yes``, the source archive is now removed after a successful import. | ||||||||||||
| Archives are preserved if any file in the archive was not imported (e.g. | ||||||||||||
| skipped as a duplicate, or the import was aborted), and in non-move import | ||||||||||||
| modes. | ||||||||||||
| - :doc:`plugins/fromfilename`: Support ``track`` prefix when parsing the track | ||||||||||||
| number from the filename (e.g., ``track01.m4a``). | ||||||||||||
| - **Tidal plugin**: Introduces a new plugin for fetching metadata from Tidal. It | ||||||||||||
| supports album and track lookups by ID, including batch operations via | ||||||||||||
| ``albums_for_ids`` and ``tracks_for_ids``. It also enables search by query as | ||||||||||||
| well as identifier-based retrieval, with support for ISRC codes (tracks) and | ||||||||||||
| barcode/EANs (albums). | ||||||||||||
|
|
||||||||||||
| This is an initial, relatively minimal implementation, but already fully | ||||||||||||
| usable for common metadata workflows. We welcome feedback, improvement ideas, | ||||||||||||
| and community contributions to further extend its capabilities. | ||||||||||||
|
|
||||||||||||
| See :doc:`plugins/tidal` for more information. | ||||||||||||
|
|
||||||||||||
| - Add support for adding or modifying a subtitle (ID3 tag ``TIT3``) field | ||||||||||||
|
|
||||||||||||
| Bug fixes | ||||||||||||
| ~~~~~~~~~ | ||||||||||||
|
|
||||||||||||
| - :ref:`import-cmd`: Multi-disc album detection now recognizes ``cassette``, | ||||||||||||
| ``digital media``, and ``vinyl`` as disc markers (e.g. ``vinyl 1``, ``12 vinyl | ||||||||||||
| 2``), in addition to the existing ``disc``, ``disk``, and ``cd`` markers. | ||||||||||||
| - :ref:`import-cmd`: Tags with a zero distance penalty are no longer shown as | ||||||||||||
| differences in the match display. Previously, custom ``distance_weights`` | ||||||||||||
| could cause fields with no actual mismatch to appear in the ``≠`` line. | ||||||||||||
| - Library path migration now also handles manually edited database rows where | ||||||||||||
| item or album-art paths were stored as SQLite ``TEXT`` values instead of | ||||||||||||
| bytes, so upgrading to the portable-path storage format no longer fails for | ||||||||||||
| those libraries. :bug:`6561` | ||||||||||||
| - :ref:`import-cmd`: Fix duplicate album art files (e.g. ``cover.2.jpg``) being | ||||||||||||
| created when re-importing albums with the :doc:`plugins/fetchart` plugin | ||||||||||||
| enabled. Old album art is now properly removed when replacing duplicate albums | ||||||||||||
| during import. :bug:`1264` :bug:`6205` | ||||||||||||
| - :doc:`plugins/discogs`: Prevent duplicate featured artists in track artist | ||||||||||||
| fields when the same artist is credited both in ``artists`` (for example with | ||||||||||||
| ``Feat.`` join text) and ``extraartists`` as ``Featuring``. :bug:`6166` | ||||||||||||
| - :ref:`import-cmd`: Metadata source plugin ID lookups now correctly call each | ||||||||||||
| plugin's own lookup method when running in parallel. :bug:`6583` | ||||||||||||
| - Improve ``DBAccessError`` messages to help users diagnose database permission | ||||||||||||
| issues more easily. The error message now mentions directory missing and file | ||||||||||||
| permissions as potential causes. :bug:`1676` | ||||||||||||
| - :doc:`plugins/lyrics`: Fix apostrophe handling in the ``musixmatch`` backend | ||||||||||||
| slug. :bug:`4759` | ||||||||||||
| - :ref:`import-cmd`: With ``original_date: yes``, album-level ``year``, | ||||||||||||
| ``month``, and ``day`` now use the original release date. :bug:`6577` | ||||||||||||
| - :doc:`plugins/musicbrainz`: Correctly handle release dates where leading or | ||||||||||||
| intermediate components are missing, e.g. 2008-??-02 | ||||||||||||
| - :doc:`plugins/badfiles`: Respect quiet mode (the ``--quiet`` flag or | ||||||||||||
| ``import.quiet: yes`` config) during import so the corrupt-file prompt is | ||||||||||||
| suppressed in non-interactive imports. :bug:`4736` | ||||||||||||
|
|
||||||||||||
| .. | ||||||||||||
| For plugin developers | ||||||||||||
| ~~~~~~~~~~~~~~~~~~~~~ | ||||||||||||
|
|
||||||||||||
| Other changes | ||||||||||||
| ~~~~~~~~~~~~~ | ||||||||||||
|
|
||||||||||||
| - :doc:`plugins/spotify`: Batch ``spotifysync`` track and audio-features API | ||||||||||||
| requests and deduplicate repeated Spotify track IDs within a run. | ||||||||||||
|
|
||||||||||||
| 2.10.0 (April 19, 2026) | ||||||||||||
| ----------------------- | ||||||||||||
|
|
||||||||||||
| New features | ||||||||||||
| ~~~~~~~~~~~~ | ||||||||||||
|
|
||||||||||||
| - **Beets library is now made portable**: item and album-art paths are now | ||||||||||||
| stored relative to the library root in the database while remaining absolute | ||||||||||||
| in the rest of beets. Path queries continue matching both library-relative | ||||||||||||
| paths and absolute paths under the currently configured music directory under | ||||||||||||
| the new storage model. The existing paths in the database are migrated | ||||||||||||
| automatically the first time you run any ``beet`` command after the update. | ||||||||||||
| :bug:`133` | ||||||||||||
|
|
||||||||||||
| .. warning:: | ||||||||||||
|
|
||||||||||||
| make sure you run ``beet version`` (or any other command) at least once | ||||||||||||
| after upgrading to trigger the migration. Only then you can safely move | ||||||||||||
| the library to a new location. | ||||||||||||
|
|
||||||||||||
| - :doc:`plugins/inline`: Add access to the ``album`` or ``item`` object as | ||||||||||||
| ``db_obj`` in inline fields. | ||||||||||||
| - :doc:`plugins/discogs`: Import Discogs remixer, lyricist, composer, and | ||||||||||||
| arranger credits into the multi-value ``remixers``, ``lyricists``, | ||||||||||||
| ``composers``, and ``arrangers`` fields. :bug:`6380` | ||||||||||||
| - :doc:`plugins/lyrics`: Add ``keep_synced`` config option and ``--keep-synced`` | ||||||||||||
| CLI flag to skip re-fetching lyrics for tracks that already have synced | ||||||||||||
| lyrics, even when ``force`` is enabled. :bug:`5249` | ||||||||||||
| - :doc:`plugins/musicbrainz`: Use aliases for artist credit. | ||||||||||||
| - Metadata source plugin searches and lookups are now executed concurrently, | ||||||||||||
| speeding up lookups when multiple plugins (e.g. MusicBrainz and Spotify) are | ||||||||||||
| enabled. | ||||||||||||
|
|
||||||||||||
| Bug fixes | ||||||||||||
| ~~~~~~~~~ | ||||||||||||
|
|
||||||||||||
| - :ref:`import-cmd` Automatically remux WAV files containing MP3 streams | ||||||||||||
| (``WAVE_FORMAT_MPEGLAYER3``) to proper MP3 files during import, instead of | ||||||||||||
| silently importing them with incorrect metadata. :bug:`6455` | ||||||||||||
| - :doc:`plugins/listenbrainz`: Retry listenbrainz requests for temporary | ||||||||||||
| failures. | ||||||||||||
| - :doc:`plugins/chroma`: Do not produce MusicBrainz-sourced autotagger | ||||||||||||
| candidates when the :doc:`plugins/musicbrainz` plugin is not enabled. The | ||||||||||||
| chroma plugin now looks up the musicbrainz plugin through the metadata-source | ||||||||||||
| registry instead of unconditionally instantiating its own private instance, | ||||||||||||
| which also restores compatibility with :doc:`plugins/mbpseudo` for | ||||||||||||
| chroma-triggered lookups. :bug:`6212` :bug:`6441` | ||||||||||||
| - :ref:`import-cmd` Remove clutter from imported album folders. :bug:`5016` | ||||||||||||
| - :doc:`plugins/web`: Fix a stored XSS vulnerability where unescaped metadata | ||||||||||||
| fields (artist, album, title, comments, lyrics) could execute arbitrary | ||||||||||||
| JavaScript in the browser. Template tags now use ``<%-`` (escaped | ||||||||||||
| interpolation) instead of ``<%=`` (raw interpolation). | ||||||||||||
|
|
||||||||||||
| For plugin developers | ||||||||||||
| ~~~~~~~~~~~~~~~~~~~~~ | ||||||||||||
|
|
||||||||||||
| - Consumers of :py:class:`beetsplug._utils.musicbrainz.MusicBrainzAPI` now | ||||||||||||
| receive normalized MusicBrainz payloads with underscore-separated field names | ||||||||||||
| (for example ``artist_credit`` and ``release_group``) and grouped relation | ||||||||||||
| lists such as ``work_relations``, ``release_relations``, and | ||||||||||||
| ``url_relations``. The API responses are also now fully typed with concrete | ||||||||||||
| ``TypedDict`` models for releases, recordings, works, and relations. Update | ||||||||||||
| direct access to raw MusicBrainz response keys if needed. | ||||||||||||
|
|
||||||||||||
| .. | ||||||||||||
| Other changes | ||||||||||||
| ~~~~~~~~~~~~~ | ||||||||||||
|
|
||||||||||||
| 2.9.0 (April 11, 2026) | ||||||||||||
| ---------------------- | ||||||||||||
|
|
||||||||||||
| Beets now officially supports Python 3.14. | ||||||||||||
|
|
||||||||||||
| New features | ||||||||||||
| ~~~~~~~~~~~~ | ||||||||||||
|
|
||||||||||||
| - :ref:`import-cmd` Use ffprobe to recognize format of any import music file | ||||||||||||
| that has no extension. If the file cannot be recognized as a music file, leave | ||||||||||||
| it alone. :bug:`4881` | ||||||||||||
|
|
@@ -183,27 +34,16 @@ New features | |||||||||||
| ``arranger`` fields. Existing libraries are migrated automatically, and | ||||||||||||
| :doc:`plugins/musicbrainz` now preserves each MusicBrainz ``remixer``, | ||||||||||||
| ``lyricist``, ``composer``, and ``arranger`` relation as a separate value. | ||||||||||||
| - :doc:`plugins/musicbrainz`: Store MBIDs for remixers, lyricists, composers, | ||||||||||||
| and arrangers in the new multi-valued fields ``remixers_mbid``, | ||||||||||||
| ``lyricists_mbid``, ``composers_mbid``, and ``arrangers_mbid``. :bug:`5698` | ||||||||||||
| :bug:`5698` | ||||||||||||
| - :doc:`plugins/replaygain`: Conflicting replay gain tags are now removed on | ||||||||||||
| write. RG_* tags are removed when setting R128_* and vice versa. | ||||||||||||
| - :doc:`plugins/fetchart`: Add support for WebP images. | ||||||||||||
| - :doc:`plugins/lastgenre`: Add support for a user-configurable ignorelist to | ||||||||||||
| exclude unwanted or incorrect Last.fm (or existing) genres, either per artist | ||||||||||||
| or globally :bug:`6449` | ||||||||||||
|
|
||||||||||||
| Bug fixes | ||||||||||||
| ~~~~~~~~~ | ||||||||||||
|
|
||||||||||||
| - :doc:`plugins/deezer`: Fix Various Artists albums being tagged with a | ||||||||||||
| localized string instead of the configured ``va_name``. Detection now uses | ||||||||||||
| Deezer's artist ID rather than the artist name string. :bug:`4956` | ||||||||||||
| - :doc:`plugins/listenbrainz`: Paginate through all ListenBrainz listens instead | ||||||||||||
| of fetching only 25, aggregate individual listen events into correct play | ||||||||||||
| counts, use ``recording_mbid`` from the ListenBrainz mapping when available, | ||||||||||||
| and avoid per-listen MusicBrainz API lookups that caused imports to hang on | ||||||||||||
| large listen histories. :bug:`6469` | ||||||||||||
| - Correctly handle semicolon-delimited genre values from externally-tagged | ||||||||||||
| files. :bug:`6450` | ||||||||||||
| - :doc:`plugins/listenbrainz`: Fix ``lbimport`` crashing when ListenBrainz | ||||||||||||
|
|
@@ -227,12 +67,12 @@ Bug fixes | |||||||||||
| switch to the plural field names. :ref:`list-cmd`, and query expressions, | ||||||||||||
| accept the same legacy singular field names and warn users to switch to the | ||||||||||||
| plural field names. :bug:`6483` | ||||||||||||
| - :doc:`plugins/fetchart`: Error when a configured source does not exist or | ||||||||||||
| sources configuration is empty. :bug:`6336` | ||||||||||||
| - :doc:`plugins/rewrite` :doc:`plugins/advancedrewrite`: Fix rewriting | ||||||||||||
| multi-valued fields such as ``genres`` by applying rules to each matching list | ||||||||||||
| entry. Additionally, apply rewrite rules in config order, so that multiple | ||||||||||||
| rules can be applied to the same field. :bug:`6515` | ||||||||||||
| - Improved error message when the database cannot be opened. When SQLite reports | ||||||||||||
| an ``unable to open`` error, beets now suggests checking that the file or | ||||||||||||
| parent directory is writable. The original SQLite error is preserved for | ||||||||||||
| debugging. Also increased the default SQLite busy timeout from 5 s to 30 s to | ||||||||||||
| reduce ``database is locked`` errors during concurrent access, and fixed the | ||||||||||||
| ``cannot not`` typo in the generic database error message. :bug:`1676` | ||||||||||||
|
Comment on lines
+73
to
+75
|
||||||||||||
| debugging. Also increased the default SQLite busy timeout from 5 s to 30 s to | |
| reduce ``database is locked`` errors during concurrent access, and fixed the | |
| ``cannot not`` typo in the generic database error message. :bug:`1676` | |
| debugging, and the ``cannot not`` typo in the generic database error message | |
| is fixed. :bug:`1676` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
grug see Migration.CHUNK_SIZE removed. but many migrations use self.CHUNK_SIZE (ex MultiValueFieldMigration). now migration run will crash with AttributeError unless every subclass set CHUNK_SIZE. put default back (or move constant into base class those migrations share).