Skip to content

fix(pi): avoid stale ctx.ui after async health check#1036

Open
yulin0629 wants to merge 2 commits into
rohitg00:mainfrom
yulin0629:fix/pi-extension-stale-ctx
Open

fix(pi): avoid stale ctx.ui after async health check#1036
yulin0629 wants to merge 2 commits into
rohitg00:mainfrom
yulin0629:fix/pi-extension-stale-ctx

Conversation

@yulin0629

@yulin0629 yulin0629 commented Jul 9, 2026

Copy link
Copy Markdown

Summary

Fixes a race in the official pi extension where refreshStatus() awaits getHealth() and then accesses ctx.ui.setStatus. If the pi session is replaced/reloaded during that await, pi invalidates the extension context and assertActive() throws:

This extension ctx is stale after session replacement or reload

This surfaces as a noisy extension error and can skip memory-recall injection for that turn.

Fixes #1035

Change

In integrations/pi/index.ts refreshStatus:

  1. Bind ctx.ui.setStatus before the await (while ctx is still active).
  2. After the health check, call the bound setter.
  3. Wrap the post-await UI update in try/catch so status is best-effort.

No behavior change for the healthy path; only avoids throwing when the session is already gone.

Test plan

  • Compared against current main integrations/pi/index.ts
  • Deployed patched file to three local pi installs; after /reload, stale-ctx error no longer reproduces on normal prompts
  • Reviewer: run pi with extension installed, trigger prompt while health endpoint is slow, then /reload//new during await — should not throw stale ctx
  • Reviewer: healthy server still shows footer status 🧠 agentmemory

Notes

  • agentmemory connect pi currently documents manual copy of these files; published npm package does not ship integrations/ in files.
  • Minimal fix only; does not add health-request timeout (optional follow-up).

Summary by CodeRabbit

  • Bug Fixes
    • Improved refresh status handling to prevent failures when the app UI is replaced or temporarily unavailable.
    • Status updates are now applied more safely during health checks, ensuring the refresh flow continues smoothly even if the UI reference becomes stale.

refreshStatus awaited getHealth() then accessed ctx.ui.setStatus.
If the pi session was replaced or reloaded during that await, the
extension context is invalidated and assertActive throws:

  This extension ctx is stale after session replacement or reload

Capture setStatus while ctx is still active, and treat post-await
status updates as best-effort.

AI-Agent: Pi
AI-Session-IDs: current
Copilot AI review requested due to automatic review settings July 9, 2026 16:08
@vercel

vercel Bot commented Jul 9, 2026

Copy link
Copy Markdown

@yulin0629 is attempting to deploy a commit to the rohitg00's projects Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a781d1a0-d5f9-4547-b9b3-a9c1633232be

📥 Commits

Reviewing files that changed from the base of the PR and between 4a4dfc8 and 3f87702.

📒 Files selected for processing (1)
  • integrations/pi/index.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • integrations/pi/index.ts

📝 Walkthrough

Walkthrough

refreshStatus in integrations/pi/index.ts now captures ctx.ui.setStatus before awaiting getHealth(), returns early if that capture fails, and uses a separate try/catch around the later status update to avoid stale-context errors after session replacement or reload.

Changes

Pi Status Refresh Fix

Layer / File(s) Summary
Safe setStatus capture and defensive update
integrations/pi/index.ts
refreshStatus binds ctx.ui.setStatus locally before await getHealth(), returns early if the setter cannot be captured, and wraps the later status update in try/catch to avoid throwing on stale ctx.

Estimated code review effort: 1 (Trivial) | ~5 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main fix: avoiding stale ctx.ui access after an async health check.
Linked Issues check ✅ Passed The change matches #1035 by binding setStatus before await and making the post-await UI update best-effort.
Out of Scope Changes check ✅ Passed The PR stays focused on the stale ctx.ui race in refreshStatus and introduces no unrelated changes.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Fixes a race in the integrations/pi extension where refreshStatus() could throw by accessing ctx.ui after awaiting an async health check, making the status update best-effort when the pi session is replaced/reloaded mid-await.

Changes:

  • Capture ctx.ui.setStatus before awaiting getHealth() to avoid stale-context access after the await.
  • Wrap the post-await status-line update in try/catch so session replacement doesn’t surface as an extension error.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread integrations/pi/index.ts Outdated
Comment on lines +134 to +139
let setStatus: ((key: string, text: string) => void) | undefined;
try {
setStatus = ctx.ui.setStatus.bind(ctx.ui);
} catch {
return;
}

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Good catch — updated to capture const ui = ctx.ui once and bind from that single reference, so we no longer read the session-bound getter twice. Pushed in the follow-up commit.

Address review feedback: avoid reading ctx.ui twice when capturing
the status setter. Bind from a single local ui reference.

AI-Agent: Pi
AI-Session-IDs: current
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.

pi: extension throws stale ctx after await getHealth() in refreshStatus

2 participants