debug-sse
Debug Server-Sent Events (SSE) notification issues when real-time updates aren't working. Use this when mutations don't trigger frontend updates.
Installation
Follow the repository instructions for this skill and your agent. Review the files and dependencies before adding it to your workflow.
View source ↗GuidesSkill instructions
Use this guide to troubleshoot Server-Sent Events (SSE) issues.
Quick Path (80% of issues)
// turbo
-
Test SSE endpoint:
curl -N -H "Accept: text/event-stream" http://localhost:5000/api/eventsShould see:
data: {"type":"Connected",...} -
Check MartenCommitListener has case for your projection
-
Check QueryInvalidationService maps notification to query keys
-
Check ReactiveQuery uses matching
queryKeys
If still broken, continue to full debugging below.
Symptoms
- ✗ Frontend doesn't update after mutation
- ✗
ReactiveQuerydoesn't invalidate - ✗ SSE connection fails or disconnects
- ✗ Events not received in browser
Related Skills
Prerequisites:
-
/start-solution- Solution must be running to debug SSE
First Steps:
-
/verify-feature- Run basic checks (build, tests) before deep debugging
Related Debugging:
-
/debug-cache- If issue seems cache-related instead of SSE -
/doctor- Check if environment setup is correct
After Fixing:
-
/verify-feature- Confirm fix works -
/scaffold-test- Add tests to prevent regression
Debugging Steps
1. Verify SSE Endpoint is Working
Test the SSE endpoint directly:
# Terminal 1: Connect to SSE stream
curl -N -H "Accept: text/event-stream" http://localhost:5000/api/events
You should see:
: connected
data: {"type":"Connected","timestamp":"2026-01-15T20:00:00Z"}
If connection fails:
- ✗ Check API service is running
- ✗ Verify
/api/eventsendpoint exists inEventsEndpoint.cs - ✗ Check firewall/network issues
2. Verify Notifications Are Defined
Check src/Shared/BookStore.Shared/Notifications/DomainEventNotifications.cs:
// ✅ Correct - implements IDomainEventNotification
public record BookCreatedNotification(Guid Id, string Title) : IDomainEventNotification;
// ✗ Wrong - missing interface
public record BookCreatedNotification(Guid Id, string Title);
If notification is missing:
- Create notification in
DomainEventNotifications.cs - Implement
IDomainEventNotificationinterface - Include all data needed for frontend invalidation
3. Verify MartenCommitListener Configuration
Open src/BookStore.ApiService/Infrastructure/MartenCommitListener.cs:
Check if your projection has a handler:
private async Task ProcessDocumentChangeAsync(
IDocumentChange change,
CancellationToken cancellationToken)
{
switch (change)
{
case BookProjection proj:
await HandleBookChangeAsync(proj, cancellationToken);
break;
// ❌ Missing: Your projection case
case AuthorProjection proj:
await HandleAuthorChangeAsync(proj, cancellationToken);
break;
}
}
Check if handler sends notification:
private async Task HandleBookChangeAsync(
BookProjection book,
CancellationToken cancellationToken)
{
var notification = new BookUpdatedNotification(book.Id, book.Title);
// ✅ Correct - sends notification
await _notificationService.NotifyAsync(notification, cancellationToken);
// ✗ Wrong - forgot to send
// (no NotifyAsync call)
}
If handler is missing:
- Add case for your projection
- Create handler method that calls
NotifyAsync - Use appropriate notification type
4. Verify QueryInvalidationService Mapping
Open src/Web/BookStore.Web/Services/QueryInvalidationService.cs:
Check if notification maps to query keys:
public IEnumerable<string> GetInvalidationKeys(IDomainEventNotification notification)
{
return notification switch
{
BookCreatedNotification => new[] { "Books" },
BookUpdatedNotification => new[] { "Books" },
// ❌ Missing: Your notification
AuthorUpdatedNotification => new[] { "Authors" },
_ => Array.Empty<string>()
};
}
If mapping is missing:
- Add case for your notification type
- Return query keys that should be invalidated
- Match keys used in
ReactiveQuerysetup
5. Verify Frontend EventsService
Check browser console for SSE connection:
In Chrome DevTools:
- Open Network tab
- Look for "events" request (type: eventsource)
- Check status is "pending" (active connection)
- View "EventStream" tab to see events
If connection is closed:
- Check
EventsService.StartListening()is called inOnInitializedAsync - Verify base URL is correct
- Check for JavaScript errors
6. Verify ReactiveQuery Configuration
Check component using ReactiveQuery:
// ✅ Correct - query keys match invalidation mapping
bookQuery = new ReactiveQuery<PagedListDto<BookDto>>(
queryFn: FetchBooksAsync,
eventsService: EventsService,
invalidationService: InvalidationService,
queryKeys: new[] { "Books" }, // Matches QueryInvalidationService
onStateChanged: StateHasChanged,
logger: Logger
);
// ✗ Wrong - query keys don't match
queryKeys: new[] { "AllBooks" } // Doesn't match "Books"
If query doesn't invalidate:
- Ensure
queryKeysmatchQueryInvalidationServicemapping - Verify
EventsServiceis subscribed - Check
onStateChangedcallback is provided
7. Test End-to-End
Perform a mutation and watch the flow:
# Terminal 1: Watch SSE stream
curl -N -H "Accept: text/event-stream" http://localhost:5000/api/events
# Terminal 2: Trigger mutation
curl -X POST http://localhost:5000/api/admin/books \
-H "Content-Type: application/json" \
-d '{"title":"Test Book",...}'
Expected flow:
- Command executed
- Event stored in Marten
-
MartenCommitListenertriggered - Notification sent via SSE
- Browser receives event
-
QueryInvalidationServicemaps to keys -
ReactiveQueryinvalidates - Query refetches
- UI updates
If any step fails, locate where:
- Check logs in Aspire dashboard
- Add debug logging to
MartenCommitListener - Use browser console to see received events
Common Issues & Fixes
Issue: Events Not Sent
Symptom: MartenCommitListener not triggered
Fix:
- Ensure
MartenCommitListeneris registered in DI - Check Marten event store configuration
- Verify projection lifecycle (
InlinevsAsync)
Issue: Wrong Event Type
Symptom: Notification sent but frontend doesn't invalidate
Fix:
// Check notification type name matches exactly
case "BookUpdatedNotification": // ✅ Correct
case "BookUpdated": // ✗ Wrong
Issue: Multiple Tabs Don't Update
Symptom: Updates only visible in tab that made change
Fix:
- SSE works per-connection, each tab needs own connection
- Each tab should call
EventsService.StartListening() - Verify SignalR isn't being used (project uses SSE)
Issue: SSE Connection Drops
Symptom: Connection works then stops
Fix:
- Check server-side timeout configuration
- Verify no proxy/load balancer kills long connections
- Add reconnection logic in
EventsService
Verification Checklist
- SSE endpoint accessible at
/api/events - Notification class implements
IDomainEventNotification -
MartenCommitListenerhas handler for projection - Handler calls
NotifyAsyncwith notification -
QueryInvalidationServicemaps notification to keys - Frontend
ReactiveQueryuses matching query keys -
EventsService.StartListening()called on mount - Browser DevTools shows active EventSource connection
- End-to-end test confirms UI updates after mutation
Debugging Tools
Backend:
- Aspire Dashboard → Structured Logs → Filter by "notification"
- Add logging in
MartenCommitListener:_logger.LogInformation("Sending {Type}", notification.GetType().Name)
Frontend:
- Browser Console → Look for EventSource logs
- React DevTools → Check component re-renders
- Network tab → Verify EventSource connection
Related Skills
First Steps:
-
/verify-feature- Run basic checks (build, tests) before deep debugging
Related Debugging:
-
/debug-cache- If issue seems cache-related instead of SSE -
/doctor- Check if environment setup is correct
After Fixing:
-
/verify-feature- Confirm fix works -
/scaffold-test- Add tests to prevent regression
See Also:
- scaffold-write - SSE notification setup in MartenCommitListener
- scaffold-frontend-feature - Frontend SSE integration
- real-time-notifications - SSE architecture and data flow
- ApiService AGENTS.md - Backend notification patterns
- Web AGENTS.md - Frontend SSE patterns
Examples
Read the usage examples in the source repository.
View source ↗Source history
- Indexed on
- 2026-01-20
- Index updated
- 2026-01-26
- Branch
- main
Index and sync dates describe this directory. View the source repository for code history.
GitHub ↗chat Comments (0)
Sign in to join the discussion and leave a comment.
No comments yet. Be the first to share your thoughts!