When generating a newsletter, the generated prompt shows "## Active Sources (0 total)" with no source URLs listed below it, even though the user has selected sources in the dashboard. This results in the AI agent receiving an empty source list and being unable to generate a meaningful newsletter because it has no sources to search or analyze.
The bug manifests in the prompt template where:
## Active Sources (0 total)
Should instead show something like:
## Active Sources (5 total)
- NICE - HTA - https://www.nice.org.uk (Important)
- FDA - Regulatory - https://www.fda.gov (Important)
...
The newsletter generation process creates a prompt for the Claude AI agent, but the sources array used to populate the "Active Sources" section is empty (sources.length === 0), even when the user has selected sources in the UI. This prevents the AI from knowing which sources to search and analyze, resulting in either a failed generation or a generic newsletter without actual data from the intended sources.
The root cause is that either:
- The selected source IDs are not being properly passed from the frontend to the backend
- The database query to fetch sources is failing or returning no results
- The source data is lost between the
/api/generatePOST endpoint and the/api/generate/streamGET endpoint
Investigate and fix the data flow from source selection in the frontend through to the prompt generation in the streaming endpoint. Ensure that:
- Selected source IDs are correctly passed in the POST request to
/api/generate - Source IDs are properly stored in the
newsletterGenerationrecord'sconfigfield - The streaming endpoint correctly retrieves source IDs from the generation config
- The database query successfully fetches the source records
- Add validation and error handling to catch and report when sources are missing at each step
- Add a check to prevent newsletter generation when no sources are selected or found
- Navigate to https://catalyst-newsletter-1078437195105.europe-west1.run.app/login
- Log in with admin credentials
- Navigate to /dashboard
- Verify that sources are displayed and selected (checkboxes should be checked)
- Click "Generate Newsletter" button
- Observe that generation proceeds but the prompt shows "## Active Sources (0 total)"
- The generated newsletter is generic and does not include real data from the sources
After analyzing the code flow, the issue can occur at multiple points:
Data Flow:
- Frontend (dashboard/page.tsx:98-106): User clicks "Generate Newsletter" → POST to
/api/generatewithselectedSources: config.selectedSources - Backend
/api/generate(route.ts:11): ReceivesselectedSourcesarray → Queries database for sources (line 14-21) → Creates generation record withconfig(line 48-55) - Backend
/api/generate/stream(route.ts:133-145): Retrieves generation record → ExtractsselectedSourcesfrom config (line 134) → Queries database for sources (line 136-145) - Prompt Generation (route.ts:150-152): Creates source list string → Builds prompt with sources (line 168-169)
Potential Root Causes:
-
Empty selectedSources array: If
config.selectedSourcesin the frontend is empty[], no sources will be queried- Location:
/app/dashboard/page.tsx:43- Initial state - Location:
/app/dashboard/page.tsx:74-77- Sources are set after fetching - Risk: If
fetchSources()fails or returns empty array, selectedSources remains[]
- Location:
-
Database query failure: The Prisma query at
/app/api/generate/route.ts:14-21might return 0 results if:- No sources have
active: truein the database - The source IDs don't exist in the database
- Database connection issues
- No sources have
-
Config not properly stored: The
agentConfigobject might not be properly serialized when stored in the database at line 48-55 of/app/api/generate/route.ts -
Config not properly retrieved: The config extraction at
/app/api/generate/stream/route.ts:133-134might fail:const config = generation.config as any const selectedSourceIds = config.selectedSources || []
If
config.selectedSourcesis undefined or null, it defaults to[] -
Second database query fails: The query at
/app/api/generate/stream/route.ts:136-145might fail even if the first query succeeded
Most Likely Cause: Based on the bug report showing "Active Sources (0 total)" appearing in the generated prompt, the sources list is reaching the prompt generation step but is empty. This suggests the issue is likely in one of these areas:
- The
config.selectedSourcesarray is empty when passed to/api/generate - The database query is failing to find matching sources (possibly due to
active: falseon sources) - The config is not being properly stored/retrieved from the database
Use these files to fix the bug:
-
/Users/clazz/Projects/Inovintell/catalyst-newsletter/app/api/generate/route.ts:8-28- POST endpoint that receives selectedSources and creates generation record. This is where we first query for sources and store the config.- Why relevant: This is where sources are first fetched from the database and where the generation record is created with the config. If sources are empty here, they'll be empty downstream.
- Key logic: Lines 14-21 query sources, lines 48-55 create generation record with config
-
/Users/clazz/Projects/Inovintell/catalyst-newsletter/app/api/generate/stream/route.ts:84-354- GET endpoint that generates the newsletter. This is where the prompt is built with sources.- Why relevant: This is where the prompt showing "Active Sources (0 total)" is generated. Lines 133-145 retrieve sources from database, lines 150-169 build the prompt.
- Key logic: Line 134 extracts selectedSourceIds from config, line 136-145 queries sources, line 168-169 shows source count in prompt
-
/Users/clazz/Projects/Inovintell/catalyst-newsletter/app/dashboard/page.tsx:65-83-fetchSources()function that loads available sources- Why relevant: This populates the sources list and sets
config.selectedSources. If this fails, no sources are selected. - Key logic: Lines 71-77 set selectedSources to all fetched source IDs
- Why relevant: This populates the sources list and sets
-
/Users/clazz/Projects/Inovintell/catalyst-newsletter/app/dashboard/page.tsx:85-175-handleGenerateNewsletter()function that sends generation request- Why relevant: This sends the selectedSources array to the backend. If the array is empty here, no sources will be used.
- Key logic: Line 100 includes
selectedSources: config.selectedSourcesin POST body
/Users/clazz/Projects/Inovintell/catalyst-newsletter/prisma/schema.prisma- Database schema for NewsSource and NewsletterGeneration models- Why relevant: Understanding the schema helps verify what fields are available and how data is stored
- Key fields:
activeboolean on NewsSource,configJSON field on NewsletterGeneration
IMPORTANT: Execute every step in order, top to bottom.
- Run a query to check if there are any active sources in the database
- Verify that sources exist and have
active: true - Document the current count of active sources
- If no active sources exist, this explains the bug immediately
- Add console.log to show received
selectedSourcesarray before database query - Add console.log to show query results (source count and IDs) after database query
- Add console.log to show the config object before creating generation record
- This will help us trace exactly where sources are lost
- After querying sources (line 21), check if
sources.length === 0 - If zero sources found, return a clear error response explaining why (no active sources, invalid IDs, etc.)
- This prevents creating a generation record with no sources
- Return HTTP 400 with detailed error message for frontend to display
- Add console.log to show generation config after retrieving from database
- Add console.log to show
selectedSourceIdsextracted from config - Add console.log to show query results (source count and IDs) after second database query
- This will show if sources are being lost between endpoints
- After querying sources (line 145), check if
sources.length === 0 - If zero sources found, send an 'error' event explaining that no sources were found
- Include diagnostic information: generationId, selectedSourceIds from config
- Do not proceed with newsletter generation if no sources available
- Update
handleGenerateNewsletter()to check ifconfig.selectedSources.length === 0before making API call - Display user-friendly error message if no sources are selected
- Prevent API call when selectedSources is empty to avoid wasting resources
- In
fetchSources(), add error logging if response is not ok - Add console.log to show how many sources were fetched and selected
- Display error message to user if source loading fails
- This helps diagnose if the issue is in source loading vs source querying
- Use Prisma Studio or direct database access to verify active sources exist
- Test newsletter generation with valid source selection
- Test newsletter generation with no sources selected (should show validation error)
- Test newsletter generation after deactivating all sources (should show validation error)
- Verify error messages are clear and actionable for the user
- Execute all validation commands to ensure the bug is fixed with zero regressions
- Verify that meaningful error messages appear when sources are missing
- Verify that newsletter generation works when sources are properly selected
Execute every command to validate the bug is fixed with zero regressions.
# Check if active sources exist in the database
cd /Users/clazz/Projects/Inovintell/catalyst-newsletter
npx prisma studio
# Manually verify: Open NewsSource table and check for records with active=true-
Test Case: No Active Sources in Database
- Deactivate all sources via Prisma Studio or admin interface
- Navigate to /dashboard
- Try to generate newsletter
- Expected: Clear error message indicating no active sources are available
- Actual: Should NOT show "Active Sources (0 total)" in generated prompt
-
Test Case: Sources Selected but Not Sent to Backend
- Activate at least 3 sources in database
- Navigate to /dashboard
- Verify sources appear and are selected
- Open browser DevTools → Network tab
- Click "Generate Newsletter"
- Check POST request to
/api/generate - Verify: Request body includes
selectedSourcesarray with numeric IDs - Verify: Response includes
sourcescount matching selection
-
Test Case: Successful Newsletter Generation
- Ensure at least 3 sources are active in database
- Navigate to /dashboard
- Verify sources are selected (should auto-select all active sources)
- Click "Generate Newsletter"
- Open browser DevTools → Console
- Check for logging output showing source count at each step
- Verify: Newsletter generation starts successfully
- Verify: Prompt shows "Active Sources (X total)" where X > 0
- Verify: Source list appears below the header with actual source URLs
-
Test Case: Frontend Validation
- Navigate to /dashboard
- Manually deselect all sources
- Click "Generate Newsletter"
- Expected: Error message appears without making API call
- Actual: User-friendly message explaining that sources must be selected
# Check Cloud Run logs for debugging output
cd /Users/clazz/Projects/Inovintell/catalyst-newsletter
gcloud logging read "resource.type=cloud_run_revision AND resource.labels.service_name=catalyst-newsletter" --limit 50 --format json | grep -E "(selectedSources|sources.length|Active Sources)"
# Or check local logs if running via docker-compose
docker-compose logs app | grep -E "(selectedSources|sources.length|Active Sources)"# Ensure TypeScript compilation succeeds
cd /Users/clazz/Projects/Inovintell/catalyst-newsletter
npm run build# After fixing, deploy and test on production
cd /Users/clazz/Projects/Inovintell/catalyst-newsletter
./deploy/gcp-deploy.sh
# Wait for deployment to complete, then test newsletter generation on production URLThis bug completely breaks the core functionality of the newsletter generation system. Without sources, the AI agent cannot:
- Search for relevant news and updates
- Aggregate information from HTA agencies
- Provide meaningful market access intelligence
- Generate valuable content for the newsletter
The generated newsletter would be generic and useless to users who expect timely, relevant HTA and market access information.
Understanding the data flow is critical to fixing this bug:
Frontend (dashboard)
└─> POST /api/generate
├─> Query DB for sources
├─> Store config with selectedSources
└─> Return generationId
Frontend (dashboard)
└─> GET /api/generate/stream?id={generationId}
├─> Retrieve generation record
├─> Extract selectedSources from config
├─> Query DB for sources AGAIN
├─> Build prompt with sources
└─> Stream newsletter generation
Key Insight: Sources are queried TWICE - once in POST endpoint, once in GET endpoint. The bug could occur at either step.
- Sources might exist but have
active: false - Source IDs in config might not match actual database IDs
- Prisma Client might not be properly initialized
- Database connection might be failing silently
config.selectedSourcesmight not be updating when sources are fetched- React state updates might be asynchronous and not completing before generation
- User might be clicking "Generate" before sources are loaded
Adding validation at multiple layers prevents this bug from manifesting silently:
- Frontend validation: Prevents wasting API calls and provides immediate feedback
- POST endpoint validation: Catches issues early and returns clear errors
- Stream endpoint validation: Last line of defense before generating with no sources
- Always validate inputs at API boundaries
- Log diagnostic information at key decision points
- Return specific, actionable error messages
- Fail fast and loud rather than proceeding with invalid state
- Add defensive coding to handle edge cases (empty arrays, null values)
After fixing this bug, consider:
- Adding integration tests that verify end-to-end source flow
- Creating a health check endpoint that verifies active sources exist
- Adding metrics/monitoring for source selection counts
- Implementing source caching to reduce database queries
- Adding Prisma query logging in development to debug database issues
- Creating a sources dashboard that shows source status and statistics