fix(channels): stream the YuanBao bot creator over binary pipes so /poll keeps its events - #912
Open
Lesereingrape wants to merge 15 commits into
Open
Lesereingrape wants to merge 15 commits into
Lesereingrape wants to merge 15 commits into
Conversation
The YuanBao bot-creator endpoints read newline-delimited JSON from the child's stdout with `parse_subprocess_json_lines`, which decodes the bytes it reads. The subprocess was started in text mode, so on Windows the pipe handed a `str` to that decoder and the first poll that had output raised `AttributeError: 'str' object has no attribute 'decode'` - taking the scan code the whole flow waits for with it. Its stderr pipe was never drained either, so a creator that died with a traceback reported `failed` with no events at all. Start the child the way the feishu creator does: binary stdout with stderr merged into it, and decode the final drain the same way.
…r-pipes CHANGELOG.md only: keep the entries added on develop since e512f05, with this fix's entry above them.
4 of 11 tasks
Contributor
Author
|
CHANGELOG 冲突已解决(本 PR 与 #910 都会插入
|
…r-pipes # Conflicts: # CHANGELOG.md
…r-pipes # Conflicts: # CHANGELOG.md
…r-pipes # Conflicts: # CHANGELOG.md
…r-pipes # Conflicts: # CHANGELOG.md
…r-pipes # Conflicts: # CHANGELOG.md
…G Unreleased overlap)
…r-pipes # Conflicts: # CHANGELOG.md
…r-pipes # Conflicts: # CHANGELOG.md
…r-pipes # Conflicts: # CHANGELOG.md
…r-pipes # Conflicts: # CHANGELOG.md
…r-pipes # Conflicts: # CHANGELOG.md
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The YuanBao bot-creator endpoints read newline-delimited JSON from the child's stdout through
parse_subprocess_json_lines, which is bytes-only —parse_json_linesdecodes what it reads (src/octop/infra/utils/subprocess_io.py:28). The subprocess was the only one insrc/started in text mode (bufsize=1, universal_newlines=True,:972-973), so on WindowsTextIOWrapper.read()handed astrto that decoder and/polldied withAttributeError: 'str' object has no attribute 'decode'atsubprocess_io.py:28on the first response that had output. The bytes were already out of the pipe at that point, and the raise happens beforestate["lines"].extend(new_lines)— so the events were dropped, not delayed: thescan_codethe wizard is waiting for never reached the dashboard.Its
stderr=subprocess.PIPE(:971) was also never read by any code path: a creator that exits with a traceback reportedstatus="failed"with an emptyeventslist and nothing to show, and an undrained pipe is the deadlock CPython'ssubprocessdocs warn about ("…the child process needs to write enough data… usestderr=STDOUT").Both halves are one root cause — the launch configuration was written against a text/stream reader, while the pipeline is a bytes-only JSON-line reader — so the fix is to start the child the way
feishu_bot_creator_start(:823-824) andinfra/gateway/bot_creators/feishu_runner.py:42-48already do: binary stdout with stderr merged into it, and the final drain decoded the same way (:1009). The JSON-line protocol, the argv, and the input sanitizers are untouched, and no other launch site insrc/is changed by this branch.Fixes #911
Related open work
Open PR #756 (
fix(channels): make the Feishu scan-to-create subprocess work on Windows, basedevelop) edits the samePopencall: at:972-974it replacesenv={**os.environ, "PYTHONUNBUFFERED": "1"}with a newpython_subprocess_env()helper, and pins the child's stdout encoding to UTF-8 inbot_creators/yuanbao_bot_creator.py. That is a different root cause — the child failing on a cp936 console encoding — and it leavesbufsize=1,universal_newlines=Trueandstderr=subprocess.PIPEexactly as they are, so both halves reported above survive it. It does mean the two branches overlap on this one call site.Re-checked before filing (2026-09-21 08:53:52Z): #756 is still open and unmerged (
head=feature/feishu-channel,base=c7a095a7, last updated2026-09-20T16:51:08Z), and this branch is rebased ontodevelop@e512f05b, which does not contain it. If #756 merges first, the one-lineenv=change is all that has to be re-applied here — I would rather rebase onto it than have this branch silently drop that fix, so say the word and I will rebase on request. I have deliberately not rebased onto #756 while it is unmerged.Also worth flagging: this is one of two PRs I am filing together (#910 is the other), and both add a bullet at the very top of
### 修复inCHANGELOG.md, so whichever of the two lands second will show a one-line conflict there. Each branch keeps its own entry so that neither PR depends on the other; resolving it is a one-line fix and I am happy to rebase whichever one you merge second.Target branch
develop(feature / fix — default)main(release/*orhotfix/*only)Type of change
Test plan
makeis unavailable on this machine, so the.githooks/pre-commitgate (make all+dashboardbuild, perAGENTS.md§6/§10) never ran here — the commits were made without it. Its sub-targets were run individually and are quoted verbatim below.The existing bot-creator tests patch
subprocess.Popenwholesale, which cannot see a pipe-configuration bug. The three new cases therefore spawn a real stub child and drive the two endpoints overTestClient, following the_make_app/mock_server_and_useridiom already in that file.RED — measured today by running this branch's
tests/unit/gateway/test_channels_qr.pyunchanged inside apristine
develop@e512f05bworktree (only the test file copied, no source change), Windows / Python 3.12:Behaviour, the shared reader fed the two launch configs side by side (no server,
PYTHONPATH=src python probe.py):Same stub child, but driven through the two real endpoints (
TestClient(..., raise_server_exceptions=False), so thestatus the app returns is visible), pristine
developvs this branch:Final state on this branch (Windows / Python 3.12;
make testequivalents):tests/unit/gateway/test_channels_qr.pyOnly Windows was measured here. Reading the two platform branches of
read_available_bytes:_keeps_the_reader_contractand_surfaces_stderrshould fail on Linux too (a text-mode pipe is text mode everywhere, andread_available_posixnever touchesstderr), whiletest_yuanbao_creator_poll_reports_eventsshould pass there before the change —os.readreturns bytes regardless of the wrapper — which is why the invariant test exists next to it, and why the Windows job is the one that actually catches the crash.Checklist
CHANGELOG.md— one bullet at the top of### 修复under## [Unreleased], citing(#911), landed as commitc57dca17Not tested here: no live server and no real YuanBao account, so the creator child in these tests is a stub script rather than
yuanbao_bot_creator.py; the >64 KB pipe-fill block is cited from thesubprocessdocs rather than measured (the swallowed-stderr half is measured); CI's Linux leg was not run locally.