Repository navigation
Description
Area: apps/server (packages/provider-pi/src/server/adapter.ts)
Steps to reproduce
- Run a Pi thread with several real user turns to completion (turns 1–5, all strong
nativeTurnRef). - Have a Pi extension with background work in the session: each wake (monitor notification / goal continuation) gets adopted as a T3 continuation run with a synthetic user message. Each adopted turn keeps a weak synthetic
nativeTurnRefpermanently. - Let the session restart at least once so the thread spans multiple Pi session files (e.g. idle-session release, server restart, or wake-driven re-spawn).
- Fork from the last real user turn (e.g. via a side chat).
Expected behavior
The fork succeeds: the forked session contains the target turn and discards the adopted-wake turns.
Actual behavior
Every fork from that turn fails permanently:
pi provider protocol error: Pi turn 6 has no captured session-tree entry and none was found in the session tree
(turn 6 = the first adopted-wake turn after the target)
Impact
Major degradation or frequent failure
Version or commit
v0.1.7-nightly.20261031.3991; same code on current main
Environment
Linux x64, Pi 2026.10.27-1, T3-launched Pi provider with a background-work extension (custom memory plugin) loaded
Logs or stack traces
pi provider protocol error: Pi turn 6 has no captured session-tree entry and none was found in the session treeAnalysis
State from statev2.sqlite (orchestration_v2_projection_provider_turns.payload_json):
| turn | ref |
|---|---|
| 1,3,4,5 | strong native ids (real user turns) |
| 6–18 | weak synthetic pending refs (adopted wakes) |
Forking from turn 5:
piForkBoundarypicks turn 6 as the boundary (first later turn with a non-null ref); its ref is weak, so the boundary isuncaptured.piUncapturedForkEntryrequireslisting.entries.has(stopAt)wherestopAtis turn 5's strong native id (adapter.ts:3266). Turn 5's entry lives in an earlier Pi session file; the live session's tree does not contain it, so the lookup returnsundefinedandpiForkEntrythrowsProviderAdapterProtocolError(adapter.ts:3238) with no further fallback.
This is a third variant the previous fixes did not cover:
- [Bug]: Pi: fork and revert fail permanently past a turn whose session entry was never captured #20530 / fix(pi): fork and revert past turns whose session entry was never captured #20521 recover a boundary turn whose entry is on the active branch of the same session.
- [Bug]: Pi: forking from a turn fails while the next turn is still running #23050 / fix(pi): fork completed turns while the next turn is running #23052 handle a still-running boundary turn.
- Neither handles a fork target whose strong ref is in a different (previous) Pi session than the live one.
Possible fixes
When stopAt is absent from the live session tree, fall back to loading the previous Pi session that contains the target's entry (it is on disk), or seed the fork from the projected thread history the way context handoff does, instead of failing closed.
AI-generated by @hong-tm's agent (opencode), posted under his instruction.
Activity
StiensWout commentedon Nov 9, 2026
Note
🤖 GPT 6.1 Sol responding on behalf of StiensWout
The maintainer closed our independent #33375 and is retaining #28538 for the same previous-session fork failure, citing its smaller diff and native adapter-to-Pi verification. Our PR retains additional snapshot/history cases and explicit split-proof limits. Once a build contains the retained fix, could you retry the historical-session fork and close this issue if it preserves exactly the target history after that fix lands?
juliusmarminge commentedon Nov 11, 2026
Fixed by #27729, now on main: When Pi cannot locate the fork boundary in its current session tree, the fork now recovers with projected history through the selected turn, excluding later wakes, instead of failing permanently.