Skip to content

bug(desktop): "Stale read from <Show>" crash when running multiple agents — v1.2.20 #16323

Closed as not planned

Description

@cpkt9762

Description

The desktop app crashes with Error: Stale read from <Show> when running ~4 agents simultaneously. Also observed on startup when a previous session with active agents is restored. Version 1.2.20.

The v1.2.18–v1.2.20 fixes (152df242, 7665b8e3, cf425d11) addressed 4 files, but 17+ non-keyed callback-form <Show> components remain unfixed.

Steps to Reproduce

  1. Open a session
  2. Run ~4 agents in parallel (e.g., multiple Sisyphus-Junior tasks)
  3. App crashes with the error page showing "Stale read from <Show>"

Stack Trace

Error: Stale read from <Show>.
ke@tauri://localhost/assets/index-BdH7uL2O.js:1:10362
ge@tauri://localhost/assets/index-BdH7uL2O.js:1:10588
po@tauri://localhost/assets/index-BdH7uL2O.js:1:6910
ie@tauri://localhost/assets/index-BdH7uL2O.js:1:6485
je@tauri://localhost/assets/index-BdH7uL2O.js:1:7919
Go@tauri://localhost/assets/index-BdH7uL2O.js:1:8820
wt@tauri://localhost/assets/index-BdH7uL2O.js:1:8193
O@tauri://localhost/assets/index-BdH7uL2O.js:1:8110
@tauri://localhost/assets/index-BX0_ILPZ.js:2306:44485

Root Cause

@solidjs/router v0.15.4 uses startTransition() internally for all navigation, triggering solidjs/solid#2046.

When a <Show> uses the callback accessor form ({(item) => ...item()...} without keyed), SolidJS passes an accessor that checks untrack(condition) before returning the value. During a transition, the condition can become false on the "old" reactive graph while the accessor is still being called — throwing the error.

Running multiple agents causes rapid state updates (messages, tool calls, status changes) which increases the likelihood of hitting this race condition via the router's transition system.

Remaining Unfixed Instances (v1.2.20)

All use <Show when={...}>{(item) => ...item()...}</Show> without keyed:

File Line when condition Startup path?
session-header.tsx 359 centerMount() ✅
session-header.tsx 379 hotkey() ✅
session-header.tsx 388 rightMount() ✅
session-new-view.tsx 65 sync.project ✅
session-context-usage.tsx 76 context() ✅
sidebar-project.tsx 46 project() ✅
sidebar-workspace.tsx 79 label() ✅
message-timeline.tsx 734 comment().selection
context-items.tsx 55, 77 item.selection, item.comment
model-tooltip.tsx 80 inputs()
settings-providers.tsx 192 note(item.id)
session-context-tab.tsx 319 systemPrompt()
settings-general.tsx 506 linux()
server-row.tsx 92 props.conn
error.tsx 312, 327 store.actionError, platform.version
desktop/index.tsx (ServerGate) 484 serverData ✅

Suggested Fix

Same pattern already applied in v1.2.18 for layout.tsx, terminal-panel.tsx, etc. — add keyed and change item() to item:

// ❌ Vulnerable to stale read during transitions
<Show when={centerMount()}>
  {(mount) => <Portal mount={mount()}>...</Portal>}
</Show>

// ✅ Fixed — keyed passes value directly, no accessor check
<Show when={centerMount()} keyed>
  {(mount) => <Portal mount={mount}>...</Portal>}
</Show>

Fix History

Version What was fixed
v1.2.18 layout.tsx, message-timeline.tsx, terminal-panel.tsx, session-side-panel.tsx — added keyed
v1.2.20 Removed startTransition from createTimelineStaging
Current 17+ instances still unfixed

Environment

  • App version: 1.2.20
  • Platform: macOS (Tauri desktop)
  • SolidJS Router: 0.15.4 (uses startTransition internally)
  • Trigger: Running ~4 agents in parallel

Activity

  1. github-actions commented on Mar 6, 2026

    @github-actions
    Contributor

    This issue might be a duplicate of existing issues. Please check:

    Your analysis of remaining unfixed instances is valuable — if the above issues don't fully cover this, consider linking to them for context.

  2. cyberprophet commented on Mar 7, 2026

    @cyberprophet
    Contributor

    Status Update

    The startTransition removal in cf425d11 (PR #16236) mitigated the most common trigger, but the 17+ unfixed <Show> instances listed in this issue remain.

    More immediately, the session-header.tsx component has a separate but related crash (#16459, #16467): current() returns undefined when options() is empty, causing current().id to throw. A fix PR is incoming for that specific crash.

    The broader keyed fix was reverted (2094e8b2, eeeb21ff) on Mar 5 — likely due to side effects from changing item() → item in callback bodies. Any future keyed fix attempt needs to ensure all item() accessor calls inside the callback are updated to item (direct value) simultaneously.

  3. chris-hatton commented on Apr 21, 2026

    @chris-hatton

    +1 Affected - navigating into / out from sub-agents while multiple agents are running seems to be a hot-bed of very easy to reproduce crashes over recent versions; can these kinds of use-cases not have automated E2E test cases?

  4. cpkt9762 commented on Apr 23, 2026

    @cpkt9762
    Author

    Update: 3 more unfixed instances + new trigger path (clicking agent task cards)

    Hit the same crash on a newer build (index-DtwFEwRY.js). Built locally with vite build --sourcemap and mapped the minified stack back to source — trigger lands at packages/app/src/context/sync.tsx:340, inside the batch() that runs reconcile(message) + setStore("part", ...) after fetchMessages.

    Trigger UX path: clicking an agent task card (subagent invocation card rendered in the message stream as <a href="...">). Navigation fires sync to reload messages for the new session, the batch reconcile clears the old tool part, and the old <Show>'s callback accessor is read while condition has already flipped falsy inside the same batch → throw.

    3 additional non-keyed callback-form <Show> not in the OP list

    File Line when Trigger
    packages/ui/src/components/basic-tool.tsx 200–221 props.triggerHref Clicking any tool card rendered as <a href> (agent/subagent task cards, file-result cards) → session reload → batch reconcile
    packages/ui/src/components/tool-error-card.tsx 138 body() Session switch while a tool error card is mounted
    packages/ui/src/components/session-retry.tsx 67 info() Session switch while retry banner is mounted

    basic-tool.tsx is the highest-impact one — it's the base wrapper for every tool card (including agent-task cards that show subagent progress), so clicking any of them with triggerHref set hits this.

    Patch (same keyed pattern used by the earlier v1.2.18 fixes)

    // packages/ui/src/components/basic-tool.tsx
           <Show
             when={props.triggerHref}
    +        keyed
             fallback={...}
           >
             {(href) => (
               <Collapsible.Trigger
                 as="a"
    -            href={href()}
    +            href={href}
                 ...
    // packages/ui/src/components/tool-error-card.tsx
    -            <Show when={body()}>{(value) => <CardDescription>{value()}</CardDescription>}</Show>
    +            <Show when={body()} keyed>{(value) => <CardDescription>{value}</CardDescription>}</Show>
    // packages/ui/src/components/session-retry.tsx
    -              <Show when={info()}>{(line) => <div data-slot="session-turn-retry-info">{line()}</div>}</Show>
    +              <Show when={info()} keyed>{(line) => <div data-slot="session-turn-retry-info">{line}</div>}</Show>

    Verified locally: typecheck clean, crash no longer reproduces when clicking agent task cards. The sync.tsx batch reconcile path is just another way to hit the same shape described in the OP (router startTransition + non-keyed callback <Show> → solidjs/solid#2046).

  5. removed
    bugSomething isn't working
    webRelates to opencode on web / desktop
    on May 3, 2026
  6. github-actions commented on Jul 3, 2026

    @github-actions
    Contributor

    To stay organized issues are automatically closed after 60 days of no activity. If the issue is still relevant please open a new one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions