{"record":{"id":"4acbc83774390765","repo":"koel/koel","slug":"the-podcast-feed-has-not-been-updated-recently","errorCode":null,"errorMessage":"└── The podcast feed has not been updated recently, skipping.","messagePattern":"└── The podcast feed has not been updated recently, skipping\\.","errorType":"console","errorClass":null,"httpStatus":null,"severity":"info","filePath":"app/Console/Commands/SyncPodcastsCommand.php","lineNumber":49,"sourceCode":"            $this->info('No podcasts to sync.');\n\n            return self::SUCCESS;\n        }\n\n        $jobs = (int) ($this->option('jobs') ?: config('koel.sync.podcast_jobs', 4));\n        $jobs = min(max(1, $jobs), count($ids));\n\n        return $jobs === 1 ? $this->syncSequentially() : $this->syncInParallel($ids, $jobs);\n    }\n\n    private function syncSequentially(): int\n    {\n        Podcast::query()->get()->each(function (Podcast $podcast): void {\n            try {\n                $this->info(sprintf('Checking \"%s\" for new content…', $podcast->title));\n\n                if (!$this->podcastService->isPodcastObsolete($podcast)) {\n                    $this->warn('└── The podcast feed has not been updated recently, skipping.');\n\n                    return;\n                }\n\n                $this->info('└── Synchronizing episodes…');\n                $this->podcastService->refreshPodcast($podcast);\n            } catch (Throwable $e) {\n                Log::error($e);\n            }\n        });\n\n        return self::SUCCESS;\n    }\n\n    private function syncInParallel(array $ids, int $jobs): int\n    {\n        $this->info(sprintf('Syncing %d podcast(s) with %d parallel workers.', count($ids), $jobs));\n","sourceCodeStart":31,"sourceCodeEnd":67,"githubUrl":"https://github.com/koel/koel/blob/41cab99feeaf59b139699403fdfd41f0a280fb39/app/Console/Commands/SyncPodcastsCommand.php#L31-L67","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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."],"exampleFix":"# before: sync skips the podcast\nphp artisan koel:podcasts:sync\n\n# after: force re-check by aging the freshness window\nphp artisan tinker --execute=\"App\\Models\\Podcast::query()->update(['last_synced_at' => now()->subDay()]);\"\nphp artisan koel:podcasts:sync","handlingStrategy":"validation","validationCode":"// Predict the skip before running the command:\nforeach (\\App\\Models\\Podcast::all() as $podcast) {\n    $fresh = abs($podcast->last_synced_at->diffInHours(now())) < 12;\n    // $fresh === true ⇒ this podcast will print the skip notice\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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."],"tags":["koel","console","podcast","sync","conditional-skip","last-modified"],"backgroundTag":"http-304-not-modified","analyzedSha":"41cab99feeaf59b139699403fdfd41f0a280fb39","analyzedAt":"2026-08-23T01:46:48.360Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}