Repository navigation
fix(AudioTag): null-safe Diff() for OriginalReleaseDate - #161
Merged
pennydreadful merged 2 commits intoSep 10, 2026
Merged
pennydreadful merged 2 commits into
pennydreadful merged 2 commits into
Conversation
AudioTag.Diff() throws "Nullable object must have a value" when comparing a tag whose file has a populated OriginalReleaseDate against metadata where the corresponding field is null (e.g. Hardcover metadata that omits the release date for a work). The outer `?.Date != other.?.Date` check correctly passes when one side is null. But the year-only shortcut (added for ID3v2.3 which can only store a year, not the full date) then dereferences `other.OriginalReleaseDate.Value.Year` unconditionally, crashing when other is null. The downstream effect is ImportApprovedBooks bailing on the entire file import - even though tag writing isn't strictly required for the import to succeed - so no BookFile row is written for that file. Apply the year-only shortcut only when both sides have a value; otherwise fall through to the full-date else branch which already handles a null on either side. The companion Date diff a few lines above is already null-safe in this way. Repros consistently when migrating an existing Goodreads-backed install to the hardcover image: 14000+ existing audiobook files all fail to import via tag-write failure because Hardcover commonly returns null release dates where Goodreads filled them in.
axsddlr
added a commit
to axsddlr/bookshelf
that referenced
this pull request
Aug 1, 2026
- Sanitize DB connection string in MigrationController logs (pennydreadful#101) - Accept empty body as qBittorrent auth success (HTTP 204, pennydreadful#178) - Add qBittorrent API key auth support from Radarr/Sonarr - Null-safe Diff for OriginalReleaseDate in AudioTag (pennydreadful#161) - NRE fix: null guards for series/LinkItems in MetadataProfileService (pennydreadful#63) - Materialize ListIds to array before GraphQL serialization (pennydreadful#168) - Robust file enumeration with per-file error handling in DiskScanService (pennydreadful#138) - Only delete same-edition files on import upgrade (pennydreadful#173) - Replace Goodreads ID with generic Book ID in search UI (pennydreadful#88)
pennydreadful
enabled auto-merge (squash)
September 10, 2026 18:24
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
AudioTag.Diff()throwsSystem.InvalidOperationException: Nullable object must have a valuewhen the file'sOriginalReleaseDateis set but the metadata being compared against has it null (or vice versa). Three-line fix: only apply the year-only shortcut when both sides have a value.Context
Hit consistently after migrating an existing Readarr/Goodreads install to the
hardcoverimage — Hardcover metadata commonly returns null release dates for works where Goodreads filled them in. Stack trace from a live failure:Downstream effect:
ImportApprovedBooksbails on the file's import when the tag-write step throws, so noBookFilerow gets written. In my install I saw the import scan run withwriteaudiotags=Synclog a handful of explicitCouldn't import bookwarnings citing this stack trace, and ended withBookFiles = 0after metadata refresh + identification had completed for ~14k files. Settingwriteaudiotags=Nois the workaround that unblocked the import (BookFiles populated to ~13.9k cleanly); this PR is the proper fix.Root cause
The outer
?.Date != other.?.Datecheck correctly passes when one side is null. The inner ID3v2.3 year-only shortcut then dereferencesother.OriginalReleaseDate.Value.Yearunconditionally, crashing whenotheris null.The companion
Datediff a few lines above is already null-safe (usesHasValuebefore.Value); this branch is just an oversight.Fix
// Id3v2.3 tags can only store the year, not the full date if (OriginalReleaseDate.HasValue && OriginalReleaseDate.Value.Month == 1 && - OriginalReleaseDate.Value.Day == 1) + OriginalReleaseDate.Value.Day == 1 && + other.OriginalReleaseDate.HasValue) {When
otheris null, fall through to the existingelsebranch which already handlesHasValueon either side and emits a clean diff entry.Verification
hardcover-v0.4.20.129image (logs above).Datediff a few lines above.dotnet build NzbDrone.Core/Readarr.Core.csproj -c Releaseclean — 0 warnings, 0 errors.No existing unit test covers
AudioTag.Diff()in this repo (checked undersrc/for any*AudioTag*Test*files — none). Glad to add one if wanted; didn't want to scope-creep without a signal.