Skip to content

Fix session completion not rendering + diagnostic lifecycle logging#145

Merged
PureWeen merged 4 commits intomainfrom
fix/sdk-session-idle-not-emitted-after-resume
Feb 18, 2026
Merged

Fix session completion not rendering + diagnostic lifecycle logging#145
PureWeen merged 4 commits intomainfrom
fix/sdk-session-idle-not-emitted-after-resume

Conversation

@PureWeen
Copy link
Owner

Problem

Sessions appear stuck showing "Thinking" even after the Copilot SDK has delivered SessionIdleEvent and CompleteResponse has run successfully. Users must click Stop to see the buffered response.

Root Cause

UI render throttle race condition in Dashboard.razor:

  • CompleteResponse fires OnStateChanged then OnSessionComplete sequentially
  • During tool-calling turns, rapid AssistantTurnEndEvents consume the 500ms throttle window
  • When the final OnStateChanged fires, the throttle drops it → sessions list not refreshed
  • HandleCompleteScheduleRender() fires, but renders stale data

Diagnosed via new diagnostic logging — the event-diagnostics.log showed:

21:03:32  [COMPLETE] CompleteResponse executing (responseLen=550)  ← SDK worked fine
21:05:28  User reported stuck                                      ← UI never updated

Fix

  1. Throttle race fix (Dashboard.razor): Refresh sessions list in HandleComplete before ScheduleRender() so the render always has current IsProcessing state.

  2. SyncContext.Post try-catch (CopilotService.Events.cs, CopilotService.cs): Wrap all SyncContext.Post callbacks in try-catch. On MAUI, exceptions in Post callbacks silently disappear — this prevented a class of bugs where event processing could fail without any trace.

  3. Diagnostic lifecycle logging (#if DEBUG only): [SEND]→[EVT]→[IDLE]→[COMPLETE] tracing with timestamps and thread IDs, written to ~/.polypilot/event-diagnostics.log. Thread-safe via lock, 10MB rotation. This caught the root cause within 1 hour of deployment.

  4. Orphaned handler detection: Warns when old event handlers (from pre-reconnect state) deliver events, with comment explaining why they're not early-returned (shared Info object).

Testing

  • 640 tests pass
  • Mac Catalyst builds clean
  • Deployed and verified: diagnostic log confirmed the throttle race theory in production
  • Verified active sessions still show correct AssistantTurnEndEvent flow

Related

PureWeen and others added 4 commits February 18, 2026 13:17
Log critical lifecycle events to ~/.polypilot/event-diagnostics.log:
- [EVT] SessionIdleEvent/TurnEnd/Error with IsProcessing state and thread ID
- [EVT-WARN] Events delivered to orphaned (replaced) state objects
- [EVT-ERR] Exceptions in SyncContext.Post callbacks and CompleteReasoningMessages
- [IDLE] CompleteResponse dispatch confirmation on UI thread
- [COMPLETE] CompleteResponse entry/skip with IsProcessing guard result
- [SEND] IsProcessing=true set point with thread ID
- [RECONNECT] Old/new state swap with session IDs
- [UI-ERR] InvokeOnUI callback failures

Also wraps SyncContext.Post callbacks in try-catch to prevent silent
exception swallowing — the most likely root cause of stuck sessions.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…es, symmetric try-catch

Fixes from GPT-5.3, Sonnet 4.6, Opus 4.6, Gemini, GPT-5 code reviews:
- Add try-catch to 'else' branches of Invoke() and InvokeOnUI() (asymmetric bug)
- Use ex.ToString() instead of ex.Message for full stack traces in diagnostics
- Add lock(_diagnosticLogLock) around File.AppendAllText for thread safety
- Add 10MB log rotation to prevent unbounded event-diagnostics.log growth
- Wrap CompleteReasoningMessages in try-catch for AssistantTurnEndEvent (was only protected in SessionIdleEvent)
- Fix 'UI thread' misnomer in IDLE log message
- Add comment explaining why orphaned handlers are not early-returned

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Move FileInfo size check inside the lock to prevent race where multiple
threads check size, all pass, and delete concurrently.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When CompleteResponse fires OnStateChanged followed by OnSessionComplete,
the RefreshState handler may be throttled (500ms window) causing the
sessions list to not be refreshed. HandleComplete then calls ScheduleRender
but renders stale data — the session still appears as 'Thinking'.

Fix: refresh sessions list in HandleComplete so the scheduled render
always has up-to-date IsProcessing state.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@PureWeen PureWeen merged commit 0150086 into main Feb 18, 2026
6 checks passed
@PureWeen PureWeen deleted the fix/sdk-session-idle-not-emitted-after-resume branch February 22, 2026 00:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant