fix: task export only works every other time#4648
Conversation
There was a problem hiding this comment.
Code Review
This pull request fixes an issue where task integrations were not loaded, causing task exports to fail. The change introduces logic in ActionItemsPage to proactively load the TaskIntegrationProvider state on initialization. This is a good fix that directly addresses the problem. I've added one comment regarding a potential race condition in the data loading logic to make it more robust.
|
@krushnarout Quick nudge: tests/demo evidence is required before review. Please add test output or a short screenshot/video, then flip this back to ready. Thanks. by AI for @beastoin |
…onnections Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
What was fixedProblem: Task auto-export to external apps (Todoist, Asana, Google Tasks, ClickUp) only worked every other time — create 2 items, only 1 exports; create 4, only 2 export. Root cause: The backend auto-sync runs in daemon threads using Fix (2 files):
DemoBefore (every other export fails): ScreenRecording_02-11-2026.09-11-15_1.MOVAfter (all exports work consistently): ScreenRecording_02-11-2026.09-17-37_1.MOV |
|
@krushnarout This PR has been inactive for 23 days. Are you still working on it? Please rebase on latest main if so — happy to review. by AI for @beastoin |
beastoin
left a comment
There was a problem hiding this comment.
Review by @ryo (community PR reviewer)
Well-diagnosed bug fix with solid root cause analysis and demo evidence.
What's good:
- Root cause is correct: daemon threads each calling
asyncio.run()share a globalhttpx.AsyncClient— when one thread's event loop closes, the client's connections go stale, producing alternating success/failure - Fix is clean: creates fresh
httpx.AsyncClientper auto-sync call viaasync with, threads an optionalclientparam through the call chain - Fully backward-compatible — existing API endpoint callers don't pass
client, so they use the global as before - Before/after demo videos clearly show the fix working
- Also fixes task integration state not loading on the action items page (Flutter side)
No issues found.
Recommendation: Safe to merge.
Summary
Root cause
The backend auto-sync spawns daemon threads that each call
asyncio.run(), creating a new event loop per call. But all threads shared a globalhttpx.AsyncClient(viaget_http_client()). When the first thread's event loop closes, the client's connections become stale. The next thread reuses the same client with dead connections → request fails. Then the stale connections get cleaned up → next call works again. This produces the alternating success/failure pattern.Fix
task_sync.py: Create a freshhttpx.AsyncClient(viaasync with) per auto-sync call instead of reusing the global singletontask_integrations.py: Add optionalclientparameter to_create_task_internal,ensure_valid_oauth_token, andrefresh_oauth_tokenso the fresh client is threaded through the entire call chainclient, so the global is used as before)Test plan
🤖 Generated with Claude Code