Troubleshooting Guide
Section titled “Troubleshooting Guide”This guide helps diagnose common production problems in NutsNews from one place.
Created for GitHub issue #30.
Issue #30 asks for a guide covering:
- Vercel build failures
- Cloudflare cache issues
- Worker 1101 errors
- Supabase save failures
- Missing thumbnails
- Better Stack logs missing
- Sentry errors
Acceptance criteria:
Common issues can be diagnosed from one document.Deployment Problems
Section titled “Deployment Problems”Use the deployment checklist when a release has failed or when you need to verify a new production deploy:
docs/DEPLOYMENT_CHECKLIST.mdThe checklist covers:
- Web deployment checks
- Worker shard deployment checks
- Controller deployment checks
- Supabase migration checks
- Cloudflare cache purge checks
- Post-deploy verification commands
Quick verification command:
./scripts/post_deploy_verify.shNutsNews App Rollout
Section titled “NutsNews App Rollout”Use this when app layer state in the portal is not what is expected.
If app rollout status is not ready:
- Confirm
NUTSNEWS_APP_ENABLEDis set only when app compose and env variables are intended to be active. - Confirm
NUTSNEWS_APP_ROUTE_ENABLEDis stillfalsewhile you validate service health. - Confirm app image repo and tag values in
NUTSNEWS_VPSworkflow/vars are correct. - Confirm
NUTSNEWS_APP_SECRET_ENV_KEYScontains the expected secret key names. - Confirm
NUTSNEWS_APP_REQUIRED_SECRETSkeys are all present and not empty inNUTSNEWS_APP_ENVS_JSON.
Rollback options:
- Stop route cutover by setting
NUTSNEWS_APP_ROUTE_ENABLED=false. - Roll app image by setting
NUTSNEWS_APP_IMAGE_TAG(orNUTSNEWS_APP_IMAGE_REPO). - Disable staged app service by setting
NUTSNEWS_APP_ENABLED=false.
NutsNews App Rollback
Section titled “NutsNews App Rollback”Use the same three controls above in reverse for a controlled rollback:
- Route disable first.
- Pin the last known good image tag.
- Disable app enablement only if full service rollback is required.
Run protected apply and review the Ansible recap after making any change.
Quick Triage
Section titled “Quick Triage”Start here when something looks wrong.
1. Check public site
Section titled “1. Check public site”curl -I "https://www.nutsnews.com/"Expected:
HTTP/2 2002. Check article API
Section titled “2. Check article API”curl -s "https://www.nutsnews.com/api/articles?page=0"Expected:
- JSON response
articlesarray- no server error
3. Check Cloudflare cache
Section titled “3. Check Cloudflare cache”./scripts/validate_cloudflare_cache_hit_rate.sh https://www.nutsnews.comExpected for public routes:
cf-cache-status: HIT4. Check a Worker shard
Section titled “4. Check a Worker shard”curl "https://nutsnews-worker-0.nutsnews.workers.dev/?limit=1"Expected:
NutsNews refresh complete5. Check controller
Section titled “5. Check controller”curl "https://nutsnews-controller.nutsnews.workers.dev/?shard=0"Expected:
NutsNews controller run completeVercel Build Failures
Section titled “Vercel Build Failures”Symptoms
Section titled “Symptoms”- Vercel deployment fails
npm run buildfails- missing environment variable error
- TypeScript error
- Sentry source map warning
Check locally
Section titled “Check locally”cd webnpm run buildCheck environment variables
Section titled “Check environment variables”NEXT_PUBLIC_SUPABASE_URLNEXT_PUBLIC_SUPABASE_ANON_KEYNEXT_PUBLIC_SENTRY_DSNNEXT_PUBLIC_APP_ENVSENTRY_ORGSENTRY_PROJECTSENTRY_AUTH_TOKENNEXTAUTH_URLNEXTAUTH_SECRETGOOGLE_CLIENT_IDGOOGLE_CLIENT_SECRETADMIN_EMAILBETTER_STACK_SOURCE_TOKENBETTER_STACK_INGESTING_HOSTCommon fixes
Section titled “Common fixes”If TypeScript fails:
cd webnpx tsc --noEmitIf lint fails:
cd webnpm run lintIf a required environment variable is missing, add it in Vercel Project Settings and redeploy.
Cloudflare Cache Issues
Section titled “Cloudflare Cache Issues”Symptoms
Section titled “Symptoms”cf-cache-status: BYPASScf-cache-status: DYNAMICcache-control: private, no-cache, no-store./scripts/validate_cloudflare_cache_hit_rate.sh https://www.nutsnews.comExpected
Section titled “Expected”Repeated public requests should eventually show:
cf-cache-status: HITage: <number>Good transitions:
MISS → HITEXPIRED → HITBYPASS → EXPIRED → HITUPDATING → HITCheck headers
Section titled “Check headers”curl -sI "https://www.nutsnews.com/" | grep -iE "cache-control|cdn-cache-control|cf-cache-status|x-nutsnews-cache"Common fixes
Section titled “Common fixes”- Confirm Cloudflare Cache Rules are enabled.
- Confirm public reader routes are eligible for cache.
- Confirm private/admin/auth routes bypass cache.
- Confirm
web/middleware.tsis deployed. - Confirm public routes return
cdn-cache-control. - Purge Cloudflare cache after major header changes.
Worker 1101 Errors
Section titled “Worker 1101 Errors”Symptoms
Section titled “Symptoms”Error 1101Worker threw exceptionCheck Worker logs
Section titled “Check Worker logs”cd workernpx wrangler tail nutsnews-worker-0Test a shard
Section titled “Test a shard”curl "https://nutsnews-worker-0.nutsnews.workers.dev/?limit=1"Check secrets
Section titled “Check secrets”SUPABASE_URLSUPABASE_SERVICE_ROLE_KEYOPENAI_API_KEYBETTER_STACK_SOURCE_TOKENBETTER_STACK_INGESTING_HOSTSENTRY_DSNCommon fixes
Section titled “Common fixes”Regenerate Wrangler configs:
cd workernpm run generate:wranglerDeploy one shard:
npx wrangler deploy --config generated-wrangler/wrangler.shard0.jsoncIf one shard works, deploy the rest.
Supabase Save Failures
Section titled “Supabase Save Failures”Symptoms
Section titled “Symptoms”reviewSaveOk: falsearticleSaveOk: falseaiUsageSaveOk: falseworkerRunSaveOk: falseCommon errors:
401 invalid API key409 duplicate key400 bad requestrelation does not existcolumn does not existCheck tables
Section titled “Check tables”select table_namefrom information_schema.tableswhere table_schema = 'public' and table_name in ( 'articles', 'article_ai_reviews', 'ai_usage_runs', 'worker_runs', 'rss_feeds', 'feed_health' )order by table_name;Check latest Worker runs
Section titled “Check latest Worker runs”select run_started_at, shard_index, success, error_name, error_message, review_save_ok, article_save_ok, ai_usage_save_ok, duration_msfrom public.worker_runsorder by run_started_at desclimit 25;Common fixes
Section titled “Common fixes”- Run
supabase db push. - Confirm Worker uses service role key, not anon key.
- Confirm migrations are applied.
- Check duplicate URL constraints.
- Check Supabase project health.
Missing Thumbnails
Section titled “Missing Thumbnails”Symptoms
Section titled “Symptoms”- Articles are fetched but not published.
- Worker rejects many items.
noThumbnailRejectedCountis high.
Check Worker response
Section titled “Check Worker response”curl "https://nutsnews-worker-0.nutsnews.workers.dev/?limit=5"Look for:
noThumbnailRejectedCountimageHydrationLookupCountimageHydrationFoundCountacceptedCountCheck weak feeds
Section titled “Check weak feeds”select *from public.bad_feedslimit 25;Common fixes
Section titled “Common fixes”- Prefer direct publisher RSS feeds.
- Disable feeds with poor image coverage.
- Use
/admin/feed-health. - Use
/admin/feeds.
Disable weak feeds:
update public.rss_feedsset is_active = falsewhere url in ( select feed_url from public.bad_feeds limit 25);Better Stack Logs Missing
Section titled “Better Stack Logs Missing”Symptoms
Section titled “Symptoms”- Worker runs but logs do not appear.
- Web app log test succeeds locally but not in Better Stack.
- Missing
nutsnews-web,nutsnews-worker, ornutsnews-controllerlogs.
Test web logs
Section titled “Test web logs”curl "https://www.nutsnews.com/api/log-test"Search Better Stack for:
service:nutsnews-webevent:api.log_test.completedTest Worker logs
Section titled “Test Worker logs”curl "https://nutsnews-worker-0.nutsnews.workers.dev/?limit=1"Search Better Stack for:
service:nutsnews-workershardIndex:0Common fixes
Section titled “Common fixes”Check:
BETTER_STACK_SOURCE_TOKENBETTER_STACK_INGESTING_HOSTMake sure secrets exist in both Vercel and Cloudflare Workers.
Sentry Errors
Section titled “Sentry Errors”Symptoms
Section titled “Symptoms”- Errors happen but do not show in Sentry.
- Source map warning during build.
- Hydration or frontend runtime errors.
Check environment variables
Section titled “Check environment variables”NEXT_PUBLIC_SENTRY_DSNSENTRY_ORGSENTRY_PROJECTSENTRY_AUTH_TOKENNEXT_PUBLIC_APP_ENVSENTRY_DSNCommon fixes
Section titled “Common fixes”- Confirm DSN belongs to the right Sentry project.
- Add
SENTRY_AUTH_TOKENin Vercel. - Confirm Sentry project slug and org slug.
- Redeploy after changing environment variables.
- Check Cloudflare Worker secret
SENTRY_DSNfor Worker-side errors.
Useful Admin Pages
Section titled “Useful Admin Pages”/admin/admin/ai-usage/admin/shards/admin/feed-health/admin/feedsUse these dashboards before digging through raw logs.
Production Health Checklist
Section titled “Production Health Checklist”Public site
Section titled “Public site”curl -I "https://www.nutsnews.com/"curl -s "https://www.nutsnews.com/api/articles?page=0"./scripts/validate_cloudflare_cache_hit_rate.sh https://www.nutsnews.comWorker
Section titled “Worker”curl "https://nutsnews-worker-0.nutsnews.workers.dev/?limit=1"Controller
Section titled “Controller”curl "https://nutsnews-controller.nutsnews.workers.dev/?shard=0"Supabase
Section titled “Supabase”select count(*) from public.articles;select count(*) from public.article_ai_reviews;select count(*) from public.worker_runs;select count(*) from public.feed_health;Better Stack
Section titled “Better Stack”Search:
service:nutsnews-webservice:nutsnews-workerservice:nutsnews-controllerSentry
Section titled “Sentry”Check:
Latest eventsRelease healthFrontend errorsServer errorsWorker errorsController and Shard Manual Testing
Section titled “Controller and Shard Manual Testing”For detailed controller and shard commands, use:
docs/CONTROLLER_AND_SHARDS.mdFast checks:
curl "https://nutsnews-controller.nutsnews.workers.dev/?shard=0"curl "https://nutsnews-worker-0.nutsnews.workers.dev/?limit=1"cd worker && npx wrangler tail --config generated-wrangler/wrangler.shard0.jsoncExpected controller response fields:
messagemodeshardCountshardRunIntervalMinutesmaxAiReviewsPerShardrequestIdshardIndexshardUrlokstatusresponseExpected healthy values:
message = NutsNews controller run completeok = truestatus = 200response.message = NutsNews refresh completeWorker Finds No Articles
Section titled “Worker Finds No Articles”Use this when Worker runs complete but publish no new articles.
Symptoms
Section titled “Symptoms”acceptedCount: 0eligibleForAiCount: 0noThumbnailRejectedCount is highalreadyReviewedCount is highimageHydrationFoundCount: 0Run a shard with extra image recovery
Section titled “Run a shard with extra image recovery”curl "https://nutsnews-worker-0.nutsnews.workers.dev/?limit=6&imageLookups=8"Fields to inspect
Section titled “Fields to inspect”fetchedCountcandidateCountalreadyReviewedCountunreviewedCountarticlePageImageLookupLimitimageHydrationLookupCountimageHydrationFoundCountnoThumbnailRejectedCounteligibleForAiCountaiReviewedCountacceptedCountrejectedCountreviewSaveOkarticleSaveOkWhat the fields mean
Section titled “What the fields mean”| Field | Meaning |
|---|---|
fetchedCount | Unique RSS articles found across the shard feeds |
candidateCount | Articles considered this run before reviewed URL filtering |
alreadyReviewedCount | Candidate URLs skipped because they were already processed or recently retried |
unreviewedCount | Candidate URLs still eligible for local/image/AI checks |
articlePageImageLookupLimit | Max article page image lookups allowed this run |
imageHydrationLookupCount | Article pages fetched to recover missing images |
imageHydrationFoundCount | Missing images recovered from article pages |
noThumbnailRejectedCount | Articles skipped before AI because no usable image was found |
eligibleForAiCount | Articles with thumbnails that passed local negative filters |
acceptedCount | Articles accepted by AI and saved for the public feed |
Common causes
Section titled “Common causes”- Most active feeds are already fully reviewed.
- Many feeds do not expose RSS thumbnails.
- Publisher pages block article-page image hydration.
- Feeds are active but stale.
- The shard has a high
alreadyReviewedCountbecause previous no-thumbnail rows were stored.
Current fix behavior
Section titled “Current fix behavior”The Worker now retries old no-thumbnail review rows after a cooldown. This prevents one bad no-thumbnail pass from blocking an article forever if image extraction later improves or the publisher page starts exposing an image.
If accepted count stays zero
Section titled “If accepted count stays zero”Check feed health:
select source, feed_url, last_article_count, last_image_count, last_accepted_count, consecutive_failure_count, last_error_messagefrom public.feed_healthorder by last_checked_at desclimit 50;Find feeds with poor image coverage:
select source, feed_url, total_article_count, total_image_count, total_accepted_countfrom public.feed_healthwhere total_article_count > 0order by (total_image_count::numeric / nullif(total_article_count, 0)) asc nulls firstlimit 25;Then prefer direct publisher feeds with stronger RSS images.
Large Failed Feed Error Bodies
Section titled “Large Failed Feed Error Bodies”Use this when manual Worker or controller responses are hard to read because failed RSS feeds return large HTML error pages.
Symptoms
Section titled “Symptoms”failedFeeds[].errorMessage contains a large HTML pageController response is hard to scanBetter Stack log payloads are noisyExpected behavior
Section titled “Expected behavior”Failed feed entries should stay readable and keep only the useful information:
{ "source": "Good News Network Good Earth", "url": "https://www.goodnewsnetwork.org/category/news/good-earth/feed/", "status": 404, "errorMessage": "HTTP 404 Not Found: Page not found - Good News Network... [truncated]"}The Worker should still complete successfully when some feeds fail:
message: NutsNews refresh completefeedFetchSuccessCount: <number>feedFetchFailureCount: <number>Verify
Section titled “Verify”curl "https://nutsnews-worker-0.nutsnews.workers.dev/?limit=1"curl "https://nutsnews-controller.nutsnews.workers.dev/?shard=0"Look for:
failedFeedsfeedFetchSuccessCountfeedFetchFailureCountNutsNews refresh completeThe failedFeeds[].errorMessage values should be capped at roughly 500 characters and should not contain full HTML documents.
