koel/koel · info
└── The podcast feed has not been updated recently, skipping
Error message
└── The podcast feed has not been updated recently, skipping.
What it means
An informational skip notice from `php artisan koel:podcasts:sync` (sequential mode, i.e. `--jobs=1` or a single podcast): `PodcastService::isPodcastObsolete()` returned false, so this podcast is deliberately not refreshed. 'Not obsolete' means either it was synced less than 12 hours ago (`last_synced_at`), or a HEAD request shows the feed's `Last-Modified` header is not newer than `last_synced_at` — i.e. the feed genuinely has no new content. This is healthy dedup behavior, not an error.
Source
Thrown at app/Console/Commands/SyncPodcastsCommand.php:49
$this->info('No podcasts to sync.');
return self::SUCCESS;
}
$jobs = (int) ($this->option('jobs') ?: config('koel.sync.podcast_jobs', 4));
$jobs = min(max(1, $jobs), count($ids));
return $jobs === 1 ? $this->syncSequentially() : $this->syncInParallel($ids, $jobs);
}
private function syncSequentially(): int
{
Podcast::query()->get()->each(function (Podcast $podcast): void {
try {
$this->info(sprintf('Checking "%s" for new content…', $podcast->title));
if (!$this->podcastService->isPodcastObsolete($podcast)) {
$this->warn('└── The podcast feed has not been updated recently, skipping.');
return;
}
$this->info('└── Synchronizing episodes…');
$this->podcastService->refreshPodcast($podcast);
} catch (Throwable $e) {
Log::error($e);
}
});
return self::SUCCESS;
}
private function syncInParallel(array $ids, int $jobs): int
{
$this->info(sprintf('Syncing %d podcast(s) with %d parallel workers.', count($ids), $jobs));
View on GitHub (pinned to 41cab99fee)
Solutions
- No action needed if you just want new episodes — the feed has none since the last sync.
- To force a re-sync regardless, age out the freshness window: set `last_synced_at` back more than 12 hours (e.g. via tinker: `$podcast->update(['last_synced_at' => now()->subDay()])`) — though the Last-Modified check can still skip it if the feed is unchanged.
- Align your cron interval with the 12-hour window (or accept the skips) since the check exists to avoid hammering feed hosts.
Example fix
# before: sync skips the podcast php artisan koel:podcasts:sync # after: force re-check by aging the freshness window php artisan tinker --execute="App\Models\Podcast::query()->update(['last_synced_at' => now()->subDay()]);" php artisan koel:podcasts:sync
Defensive patterns
Strategy: validation
Validate before calling
// Predict the skip before running the command:
foreach (\App\Models\Podcast::all() as $podcast) {
$fresh = abs($podcast->last_synced_at->diffInHours(now())) < 12;
// $fresh === true ⇒ this podcast will print the skip notice
} Prevention
- Schedule `koel:podcasts:sync` at intervals ≥ 12 hours; shorter intervals just print skips.
- Don't interpret the notice as failure — new episodes appear only when the feed's Last-Modified advances past `last_synced_at`.
- To force a full refresh (e.g. after fixing feed metadata), age `last_synced_at` past the window and re-run.
When it happens
Trigger: Running `koel:podcasts:sync` twice within 12 hours; running it after 12+ hours when the feed's `Last-Modified` (RFC 1123) predates the last sync; a feed that omits `Last-Modified` would NOT hit this branch (missing header counts as obsolete and forces a sync).
Common situations: Cron schedules running podcast sync more frequently than the 12-hour freshness window (every sync in between just prints this); operators expecting a forced refresh; investigating why new episodes 'didn't sync' when in fact the feed hasn't changed.
Related errors
- Media path hasn't been configured. Let's set it up.
- No song owner specified. Setting the first admin ($user->nam
- Changing the storage configuration can cause irreversible da
- Consider backing up your data before proceeding.
- Changing the storage configuration can cause irreversible da
AI-assisted analysis of koel/koel@41cab99fee (2026-08-23).
Data as JSON: /api/errors/4acbc83774390765.
Report an issue: GitHub.