Skip to content

Fix ArgumentException on NZB titles containing quote characters - #837

Open
lisim wants to merge 1 commit into
Listenarrs:canaryfrom
lisim:808-sabnzbd-quote-filename
Open

Fix ArgumentException on NZB titles containing quote characters#837
lisim wants to merge 1 commit into
Listenarrs:canaryfrom
lisim:808-sabnzbd-quote-filename

Conversation

@lisim

@lisim lisim commented Aug 20, 2026

Copy link
Copy Markdown

Summary

Fixes #808.

Release titles containing " (very common in usenet subject lines — embedded sub-titles, part numbers, etc.) crash the SABnzbd add-file submission with System.ArgumentException thrown from ContentDispositionHeaderValue.EncodeAndQuoteMime, before the request ever reaches SABnzbd. The download silently disappears (the provisional download record is removed on submission failure).

Root cause: GenericUsenetSourceResolver.SanitizeFileName() only strips characters invalid for a filesystem filename (Path.GetInvalidFileNameChars() — on Linux/macOS this is essentially just /). " and \ are valid filesystem characters there, so they pass through untouched, but this filename is then used as the multipart Content-Disposition filename parameter when POSTing to SABnzbd's addfile endpoint (SabnzbdAddWorkflow.AddAsync), and .NET's header-value quoting can't safely encode an unescaped ".

Note SABnzbd's actual displayed title comes from the separate nzbname query parameter (SabnzbdAddRequestPlanner.BuildFileQueryParams), not from this multipart filename, so sanitizing it further has no user-visible effect beyond fixing the crash.

Changes

Fixed

  • GenericUsenetSourceResolver.SanitizeFileName() now also replaces " and \ with _, alongside the existing filesystem-invalid-character check.

Added

  • Regression test AddAsync_TitleContainsQuotesAndCommas_SendsNzbWithoutHeaderEncodingError in SabnzbdAdapterTests.cs, using the real-world failing title from Failed to add nzb with " (or other non-ASCII charachters) in name #808/production logs, asserting the full resolve → submit pipeline succeeds.
  • An addfile mode handler on SabnzbdApiMock (previously only version/history/queue were handled; nothing exercised the add-file submission path via the shared mock).

Testing

I couldn't get a clean local dotnet test run for this test class — on this environment, every test in SabnzbdAdapterTests (18/18, including unrelated pre-existing ones) fails at InitializeAsyncAddAuthorizedRootAsync with PathIdentityState Expected: Valid, Actual: Unavailable, when run inside a stock mcr.microsoft.com/dotnet/sdk:10.0 container (tried both a plain docker run and one with --cap-add SYS_ADMIN --security-opt seccomp=unconfined; same result either way). I confirmed this is pre-existing and unrelated to this change by running the identical test file against unmodified canary HEAD in the same container — 17/17 fail the same way there too. Per .github/AGENTS.md's own guidance ("a test skipped on the current host does not validate that platform... confirmed by the authoritative native Linux CI run"), I'm relying on CI for this test class rather than trying to fix an unrelated sandboxing limitation in this PR.

To still get direct proof the actual fix works, I wrote a standalone throwaway console app (not part of this PR) that calls GenericUsenetSourceResolver.ResolveAsync with the exact failing title from #808 and then performs the same MultipartFormDataContent.Add(fileContent, "name", submission.FileName) call SabnzbdAddWorkflow makes:

  • Against unmodified canary HEAD: reproduces the exact reported exception verbatim —
    ArgumentException: The format of value 'DBS #0762 "J.K. Rowling - Harry Potter 1-7 Audio Book (english)" - "Audio book - Harry Potter And The Deathly Hallows - J.K. Rowling.part01.rar" (02_22) - 672,05 MB.nzb' is invalid.

  • Against this branch: Resolved FileName: DBS #0762 _J.K. Rowling - Harry Potter 1-7 Audio Book (english)_ - _Audio book - Harry Potter And The Deathly Hallows - J.K. Rowling.part01.rar_ (02_22) - 672,05 MB.nzbPASS: MultipartFormDataContent.Add did not throw.

  • dotnet build listenarr.slnx — clean, 0 errors.

  • dotnet format listenarr.slnx --no-restore --verify-no-changes --include <the 3 changed files> — clean, no formatting changes needed.

Review coverage (per .github/AGENTS.md)

  • Composition/DI: N/A — no service registrations, constructors, or lifetimes touched.
  • Persistence/migrations: N/A — no EF/schema changes.
  • Concurrency/cancellation: N/A — pure synchronous string transform, no new async paths.
  • Filesystem/security boundaries: reviewed — this tightens the boundary (more characters rejected before reaching an HTTP header), doesn't relax anything.
  • Serialization/identity: N/A.
  • Recovery/restart: N/A.
  • Frontend/backend contracts: N/A — backend-only, no API surface change.
  • Platform behavior: reviewed — Path.GetInvalidFileNameChars() already differs between Windows and Unix; the two added characters (", \) are additionally unsafe on both because the failure mode is in the HTTP header layer, not the filesystem layer, so this fix is platform-independent by construction.
  • Tests: added (see above).

Release titles containing " (very common in usenet subject lines,
e.g. embedded sub-titles) crashed the SABnzbd add-file submission with
System.ArgumentException from ContentDispositionHeaderValue, because
GenericUsenetSourceResolver.SanitizeFileName() only stripped
filesystem-invalid characters and left '"'/'\\' untouched. Those are
valid on Linux/macOS filesystems but break the multipart
Content-Disposition "filename" quoting used when submitting to
SABnzbd, so the download silently never reached the client.

Fixes Listenarrs#808.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
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.

Failed to add nzb with " (or other non-ASCII charachters) in name

1 participant