Repository navigation
Show mixed or multi-file diff tool output as text instead of a blank "Invalid or empty patch" block #102
Description
In Canary 0.0.11, a bash tool result that contains a multi-file git diff mixed with other output renders as "Invalid or empty patch" followed by several screens of blank space. There is no raw-text fallback, so the result can't be audited in the chat.
Observed
Channel image-paste-review, agent response at 11:33 AM. Expand the command group, then expand the bash call:
git rev-parse origin/main HEAD; git diff origin/main; gh issue view 91 --repo githubnext/ace2 | head -40The expanded result shows only "Invalid or empty patch" followed by blank space. Nate confirmed this from both the native Accessibility text and a screenshot.
Cause
The shell didn't fail. The tool result saved in the channel's database is intact: isError: false, 4,105 characters, 110 lines. It contains two commit SHAs, a valid two-file diff (chat-composer.tsx and rich-text.tsx), and then the gh issue view output.
The renderer treats the entire result as a single-file patch (all paths are at origin/main = 1ab91be, canary 0.0.11):
isPatch()inpackages/ui/src/components/blocks/tool-layout.tsmatches any result that containsdiff --git/---,+++, and@@lines anywhere, so the whole mixed output gets routed todiffBlock(data.result).parseDiff()inpackages/ui/src/lib/highlighter.tscalls@pierre/diffsgetSingularPatch. That function throwsFileDiff: Provided patch must contain exactly 1 file difffor this result.parsePatchFilessees 1 patch with 2 files.parseDiffcatches the error and returnsnull.- With
parsed === null,diff()inpackages/ui/src/components/blocks/diff.tsxstill sizes the block from the raw line count: 110 × 20 px + 12 ≈ 2,212 px.Diff(packages/ui/src/components/diff/diff.tsx) then renders the "Invalid or empty patch" box at that height, which produces the blank space. The raw text is never shown.
I reproduced this against the saved result with the bundled @pierre/diffs 1.2.10. Each file's diff parses on its own. A related silent loss: if the output has a single-file diff plus surrounding text, getSingularPatch succeeds but renders only the hunk lines. In that case the SHAs and the gh output disappear without any error.
Expected
- Tool results are always readable. If rich diff parsing fails, show the result as plain (diff-highlighted) text, the same way other results are shown.
- Non-diff output around a diff (command output, SHAs, issue text) is never dropped. Either render diff segments richly and keep the surrounding text in order, or fall back to text for the whole result.
- Multi-file diffs either render every file or fall back to text. A diff must never be treated as invalid just because it contains more than one file.
- The block height matches the content that is actually rendered. A failed parse never reserves a large blank height.
- The full raw output can still be audited even when a rich rendering is shown.
Related: #54 (tool results and full output in the channel), #99 (plain-language account with an auditable trail). Dogfooding core/QOL: #5.
Activity
fix(ui): show mixed or multi-file diff output as text (#240)