[shell] stdin_write blocks the main thread while the child isn't reading stdin
Maintainers usually reply within 3 days
Nobody has claimed this yet.
Assessment
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Newbie friendliness
- 52/100
Research direction
Start by reading the stdin_write command and the shell child management code that protects shell.children with a lock; the issue does not name file paths or tests. Compare the async kill change in issue #3351 and trace how writes from JavaScript preserve order. Done means large writes no longer block the main thread, while child cleanup and other commands can proceed and writes remain ordered; the issue leaves the design open.
Written by the indexing model from the issue text.
Description
Describe the bug
stdin_write is a sync command, so it runs on the main thread, and it does a blocking write_all into the child's stdin pipe. If the data is bigger than the pipe buffer and the child isn't reading its stdin at that moment, the main thread waits until the child reads. The whole window freezes for that time, and if the child never reads stdin it stays frozen.
It also holds shell.children.lock() during the write, so from reading the code every other shell command (spawn, kill, writes to other children) and the Terminated cleanup wait on that lock too. I only measured the main thread part.
#3351 made kill async for the same main thread reason, stdin_write is still sync.
Reproduction
Spawn sh -c 'sleep 6; cat >/dev/null' from JS, call child.write() (plugin:shell|stdin_write) with 1 MiB, then 100 ms later call any other sync command and time it.
macOS 26.5.1, Tauri 2.12.5, tauri-plugin-shell 2.6.0:
32 KiB write: the other command answers in 1 ms
1 MiB write, child reads after 6 s: the other command answers after 5918 ms and 5916 ms (2 runs)
1 MiB write, child reads after 20 s: 19918 ms
A stack sample of the 20 s run, taken while it was stuck, has the main thread in tauri_plugin_shell::commands::stdin_write > std::io::Write::write_all > write in 2605 of 2605 samples.
I can put the probe app in a repo if that helps.
Expected behavior
Writing to a child's stdin shouldn't block the main thread, at least not for longer than it takes to copy the data.
Full tauri info output
[✘] Environment
- OS: Mac OS 26.5.1 arm64 (X64)
✔ Xcode Command Line Tools: installed
✘ Xcode: not installed!
✔ rustc: 1.95.0 (59807616e 2026-04-14)
✔ cargo: 1.95.0 (f2d3ce0bd 2026-03-21)
✔ rustup: 1.29.1 (d95a37b6a 2026-08-13)
✔ Rust toolchain: stable-aarch64-apple-darwin (default)
- node: 26.8.1
- pnpm: 12.11.2
- yarn: 1.22.22
- npm: 11.19.0
- bun: 1.4.2
- deno: deno 2.9.7
[-] Packages
- tauri 🦀: 2.12.5
- tauri-build 🦀: 2.7.4
- wry 🦀: 0.57.1
- tao 🦀: 0.37.2
[-] Plugins
- tauri-plugin-shell 🦀: 2.6.0
[-] App
- build-type: build
- CSP: unset
- frontendDist: dist
Additional context
I'd like to send a fix but the shape needs a decision, which is why this is an issue first. Just making the command async moves the wait off the main thread, but the write would still hold the children lock, so a kill meant to get out of the stuck write would wait behind it. And writes from JS would need to keep their order, which they get for free today because they all run on the main thread.
One way: make it async, take what's needed to write out of children under the lock (or give each child its own writer task with a queue, so order is kept), and do the blocking write in spawn_blocking. Happy to do whichever you prefer.
- Dominant language
- Rust
- Stars
- 1.8k
- Forks
- 637
- Avg merge
- 5d 3h
- Merged PRs (30d)
- 36
Getting set up
- No Dockerfile or Docker Compose file
- No pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from tauri-apps/plugins-workspace
-
Difficulty 2/5 1-3 hours Newbie friendliness 77/100
tauri-apps/plugins-workspace#3780 ·
Maintainers usually reply within 3 days
-
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
tauri-apps/plugins-workspace#3770 ·
Maintainers usually reply within 3 days
-
[notification] init script fires IPC on about:blank → 500 'Origin header is not a valid URL' on Linux (WebKitGTK)May be free again A pull request for this issue was closed without being merged. Openplatform: linux plugin: notification type: bug
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
tauri-apps/plugins-workspace#3562 · 1 comment ·
Maintainers usually reply within 3 days
-
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
tauri-apps/plugins-workspace#3543 ·
Maintainers usually reply within 3 days
-
platform: ios plugin: notification type: bug
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
tauri-apps/plugins-workspace#3533 ·
Maintainers usually reply within 3 days
All issues in tauri-apps/plugins-workspace
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 83/100
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
NVIDIA/spark-local-inference#268 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
datafusion-contrib/StreamFusion#653 ·
Maintainers usually reply within 1 day
-
bug CLI exec session
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
tursodatabase/turso#11734 ·
Maintainers usually reply within 1 day