Skip to content

fix(sync): re-check dead feed addresses, fall back to /sync for failing feeds, follow moved feeds - #1160

Merged
ashwkun merged 2 commits into
masterfrom
feed-address-fixes
Oct 22, 2026
Merged

ashwkun merged 2 commits into
masterfrom
feed-address-fixes

Conversation

@ashwkun

@ashwkun ashwkun commented Oct 22, 2026

Copy link
Copy Markdown
Member

Summary

  • A subscribed show whose saved feed address stopped working is re-checked against the server's confirmed address, at most once a day, and read again at the new address.
  • A show whose own feed fails in a sync pass now gets its latest episode from /sync instead of nothing.
  • A feed that moved for good (permanent redirects, or itunes:new-feed-url) has its new address saved when the new feed serves the same show.
  • Each behavior has a PostHog kill switch.

Motivation

  • Every feed-address recovery path stopped as soon as any HTTPS address was saved, so a dead address was never replaced.
  • SubscriptionForegroundSync sent only shows without a saved HTTPS address to /sync. A show whose saved feed was dead got no latest episode at all: Home, notifications and auto-download stayed on an old episode.
  • OkHttp followed a moved feed's redirects, but the saved address never changed, so each pass downloaded the whole feed again through the redirect, and the address other devices and notifications use stayed old. itunes:new-feed-url wasn't read.

What changed

  • :core:rss: feed read errors are typed (FeedHttpException with the status code, NotAFeedException), both still IllegalArgumentExceptions with the same messages. FeedReadFailure.isAddressGone reads 401/403/404/410/451, no such host, a failing certificate and a non-feed body as a dead address; timeouts, 5xx, 429 and dropped connections are passing trouble. A refresh failure carries it as RefreshOutcome.Failure.addressGone.
  • :core:rss: RssFetchResult.movedTo (final address when every redirect was 301/308), ParsedRssFeed.newFeedUrl (both parsers), and confirmedMove, used when RefreshRequest.detectMove is set: a new HTTPS address that differs from the saved one and serves the same show by title. A declared address is read once to confirm; temporary redirects never move a feed.
  • :core:catalog: after the feed reads, recoverFailedDirectFeeds re-checks dead addresses (recheckGoneFeedAddress: GET /podcast, once a day per show, counted only once the server answers), reads a new address at once, and sends shows still failing to /sync through the ordinary save, which never replaces a newer tip. A confirmed move is saved through ensureHttpsFeedUrl. Never for rss: shows, whose address is their identity and notification consent.
  • :core:domain: RefreshOutcome.Failure.addressGone, RefreshOutcome.Success.movedTo, RefreshRequest.detectMove (all defaulted).
  • :app: feed-fix-address-recheck-off, feed-fix-sync-fallback-off, feed-fix-moved-feed-off kill switches passed as FeedFixToggles; each fix stays on without an answer from PostHog.
  • :core:testing: FakeLocalEpisodeCatalogPort can return a chosen refresh outcome.
  • Module READMEs updated, including the stale /sync description in :core:catalog.

Behavior & compatibility

  • No screens change, so phone, tablet, foldable and resizable-window states are unaffected.
  • A saved address changes only to an HTTPS address the server confirms, or to a permanent move that serves the same show. The change is marked for sync like any other edit.
  • Extra network use is bounded: at most one /podcast lookup per dead show per day, one extra read for a declared new address, and one /sync call per pass for failing shows.

Impact (required)

User impact — pick exactly one

  • user-impact-critical
  • user-impact-high
  • user-impact-medium
  • user-impact-low
  • no-user-impact

Listener impact — required when user-impact-critical, user-impact-high, or user-impact-medium

What changes in the user’s life:

  • Shows whose feed stopped working get new episodes again, on Home, in notifications and in automatic downloads.
  • Shows that moved to a new feed address follow it, and stop downloading the whole old feed on every check.

Backend — optional, pairable with any user-impact level

  • backend-change

Release copy (verbatim — highest priority)

CHANGELOG.md (required developer copy)

Fixed

  • Subscription sync re-checks a dead saved feed address against GET /podcast once a day per show and reads a new address at once; shows whose own feed failed get their latest episode from /sync.
  • A feed that moved for good (301/308 redirects or itunes:new-feed-url, same show title) has its new HTTPS address saved; temporary redirects and rss: shows are never moved.
  • Feed read errors are typed (FeedHttpException, NotAFeedException) and classified by FeedReadFailure; PostHog kill switches feed-fix-address-recheck-off, feed-fix-sync-fallback-off, feed-fix-moved-feed-off.

README What's New / Upcoming (required for user-impact changes)

Critical

  • Shows whose feed stopped working get new episodes again.

Fixes

  • Shows that moved to a new feed address now follow it.

Test plan

  • :core:domain, :core:testing, :core:network, :core:database, :core:rss, :core:catalog, :core:downloads, :feature:info and :app unit tests pass locally; detekt clean; :app:lintDebug with no findings in changed files
  • New coverage: FeedReadFailureTest, FeedMoveTest (redirect chains, itunes:new-feed-url, same-show check, a declared address that fails or points to itself), LocalEpisodeCatalogRepositoryTest (dead-address flag, permanent move reported only when asked and the show matches), SubscriptionForegroundSyncTest (re-check and re-read, /sync fallback and its switch, once-a-day re-check counting only answered lookups, move adoption only with the switch on and never for rss:)
  • Built and installed on a phone (./gradlew installDebug)
  • Required checks are green before merge

@ashwkun ashwkun added the user-impact-critical Critical listener-facing fix — leads README/changelog; PR release-copy used verbatim label Oct 22, 2026
@coderabbitai

coderabbitai Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

⚙️ Run configuration
📥 Commits

Reviewing files that changed from the base of the PR and between 4816d3d and 909c9d2.

📒 Files selected for processing: 19
  • app/README.md
  • app/src/main/java/cx/aswin/boxlore/AppContainer.kt
  • app/src/main/java/cx/aswin/boxlore/sync/FeedFixSwitches.kt
  • core/catalog/README.md
  • core/catalog/src/main/java/cx/aswin/boxlore/core/catalog/DirectFeedRecovery.kt
  • core/catalog/src/main/java/cx/aswin/boxlore/core/catalog/SubscriptionForegroundSync.kt
  • core/catalog/src/test/java/cx/aswin/boxlore/core/catalog/SubscriptionForegroundSyncTest.kt
  • core/domain/README.md
  • core/domain/src/main/java/cx/aswin/boxlore/core/domain/ports/LocalEpisodeCatalogPort.kt
  • core/rss/README.md
  • core/rss/src/main/java/cx/aswin/boxlore/core/rss/FeedReadFailure.kt
  • core/rss/src/main/java/cx/aswin/boxlore/core/rss/LocalEpisodeCatalogRefresh.kt
  • core/rss/src/main/java/cx/aswin/boxlore/core/rss/RssFeedClient.kt
  • core/rss/src/test/java/cx/aswin/boxlore/core/rss/FeedMoveTest.kt
  • core/rss/src/test/java/cx/aswin/boxlore/core/rss/FeedReadFailureTest.kt
  • core/rss/src/test/java/cx/aswin/boxlore/core/rss/LocalEpisodeCatalogRepositoryTest.kt
  • core/rss/src/test/resources/feed_moved.xml
  • core/testing/README.md
  • core/testing/src/main/java/cx/aswin/boxlore/core/testing/fakes/FakePorts.kt
+-------------------------------------------+
| Crossing the tensor streams to find bugs. |
+-------------------------------------------+
  \
   \     /)/)
        ( '')
        /  |
      o(___)
✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
  • 🛠️ update changelog
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@ashwkun
ashwkun force-pushed the feed-address-fixes branch 2 times, most recently from ddca3db to 5dbd45c Compare October 22, 2026 15:58
Base automatically changed from app-feed-fixes to master October 22, 2026 16:31
@ashwkun
ashwkun force-pushed the feed-address-fixes branch from 5dbd45c to 909c9d2 Compare October 22, 2026 16:32
@sonarqubecloud

Copy link
Copy Markdown

@ashwkun
ashwkun enabled auto-merge (squash) October 22, 2026 16:44
@ashwkun
ashwkun merged commit e2cc505 into master Oct 22, 2026
9 of 10 checks passed
@ashwkun
ashwkun deleted the feed-address-fixes branch October 22, 2026 16:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

user-impact-critical Critical listener-facing fix — leads README/changelog; PR release-copy used verbatim

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant