Skip to content

fix: task export only works every other time#4648

Merged
krushnarout merged 4 commits intomainfrom
fix/task-export-state
Mar 11, 2026
Merged

fix: task export only works every other time#4648
krushnarout merged 4 commits intomainfrom
fix/task-export-state

Conversation

@krushnarout
Copy link
Copy Markdown
Member

@krushnarout krushnarout commented Feb 7, 2026

Summary

  • Fix task auto-export only working every other time (1 out of 2, 2 out of 4 exports succeeding)
  • Fix task integration state not being loaded on the action items page

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 global httpx.AsyncClient (via get_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 fresh httpx.AsyncClient (via async with) per auto-sync call instead of reusing the global singleton
  • task_integrations.py: Add optional client parameter to _create_task_internal, ensure_valid_oauth_token, and refresh_oauth_token so the fresh client is threaded through the entire call chain
  • Existing API endpoint callers are unaffected (they don't pass client, so the global is used as before)

Test plan

  • Create action item → verify it appears in external app (Todoist/Asana/Google Tasks/ClickUp)
  • Create 2nd action item immediately after → verify it also exports
  • Create 4+ items in sequence → verify all export consistently

🤖 Generated with Claude Code

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread app/lib/pages/action_items/action_items_page.dart Outdated
@beastoin
Copy link
Copy Markdown
Collaborator

@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

@beastoin beastoin marked this pull request as draft February 11, 2026 01:19
krushnarout and others added 2 commits February 11, 2026 13:31
…onnections

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@krushnarout krushnarout changed the title fix load task integrations fix: task export only works every other time Feb 11, 2026
@krushnarout
Copy link
Copy Markdown
Member Author

What was fixed

Problem: 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 asyncio.run(), which creates a new event loop each time. But all threads shared a single global httpx.AsyncClient. When the first thread's event loop closed, the client's connections became stale. The next thread reused the same client with dead connections → request failed. Then stale connections got cleaned up → next call worked again, producing the alternating pattern.

Fix (2 files):

  • backend/utils/task_sync.py — Create a fresh httpx.AsyncClient per auto-sync call (via async with) instead of reusing the global singleton
  • backend/routers/task_integrations.py — Add optional client parameter to _create_task_internal, ensure_valid_oauth_token, and refresh_oauth_token so the fresh client flows through the entire call chain. Existing API endpoint callers are unaffected (backward compatible).

Demo

Before (every other export fails):

ScreenRecording_02-11-2026.09-11-15_1.MOV

After (all exports work consistently):

ScreenRecording_02-11-2026.09-17-37_1.MOV

@krushnarout krushnarout marked this pull request as ready for review February 14, 2026 14:59
@beastoin
Copy link
Copy Markdown
Collaborator

@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

Copy link
Copy Markdown
Collaborator

@beastoin beastoin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 global httpx.AsyncClient — when one thread's event loop closes, the client's connections go stale, producing alternating success/failure
  • Fix is clean: creates fresh httpx.AsyncClient per auto-sync call via async with, threads an optional client param 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.

@krushnarout krushnarout merged commit e890767 into main Mar 11, 2026
1 check passed
@krushnarout krushnarout deleted the fix/task-export-state branch March 11, 2026 14:54
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.

2 participants