
debug-sse
by aalmada
Full-stack .NET online book store application with event-sourced backend API and Blazor frontend, orchestrated by Aspire.
SKILL.md
name: debug-sse description: Debug Server-Sent Events (SSE) notification issues when real-time updates aren't working. Use this when mutations don't trigger frontend updates.
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 keysReactiveQueryinvalidates- 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
Score
Total Score
Based on repository quality metrics
SKILL.mdファイルが含まれている
ライセンスが設定されている
100文字以上の説明がある
GitHub Stars 100以上
1ヶ月以内に更新
10回以上フォークされている
オープンIssueが50未満
プログラミング言語が設定されている
1つ以上のタグが設定されている
Reviews
Reviews coming soon

