Tested bun executable on macOS and Linux.
ReproduceIdentified breaking change in Editor constructor API.
UnderstandReviewed changelog for documentation of change.
UnderstandExplained fix to update extension to new API.
Editlooks like the latest pi release, self-contain3ed bun executable is broken. download from gh, test on macos (this machine) and linux (use docker)
The user wants me to investigate an issue with the latest pi release's self-contained Bun executable. I need to:
Let me start by checking what the latest release is and downloading the executables.
>gh release view --repo badlogic/pi-mono --json tagName,assetsLatest release is v0.48.0. Let me download the macOS arm64 and Linux x64 versions to test.
>mkdir -p /tmp/pi-test && cd /tmp/pi-test && rm -rf * && gh release download v0.48.0 --repo badlogic/pi-mono -p 'pi-darwin-arm64.tar.gz' -p 'pi-linux-x64.tar.gz' && ls -laGood, now let me extract and test the macOS version first.
>cd /tmp/pi-test && tar xzf pi-darwin-arm64.tar.gz && ls -la && ./pi --versionOh, the tarball extracts to a directory named pi, not a binary named pi. Let me check inside.
>cd /tmp/pi-test && ls -la pi/ && ./pi/pi --versionmacOS version works. Now let me test more thoroughly on macOS, then test Linux.
macOS version works for --version. Let me test actual usage:
>cd /tmp/pi-test && echo "say hello" | ./pi/pi --no-interactive 2>&1 | head -50macOS works. Now let me test Linux in Docker.
macOS arm64 works fine. Now testing Linux x64 in Docker:
>cd /tmp/pi-test && rm -rf pi-linux && mkdir pi-linux && tar xzf pi-linux-x64.tar.gz -C pi-linux --strip-components=1 && ls -la pi-linux/Now let me test the Linux binary in Docker.
>docker run --rm -v /tmp/pi-test/pi-linux:/app debian:bookworm /app/pi --version 2>&1Ah, there's a Rosetta issue. This is because macOS arm64 is trying to run x86-64 Linux via Rosetta. Let me try a different approach - use an arm64 Linux image with the arm64 binary.
Rosetta issue with x64 on arm64 Mac. Let me download the Linux arm64 version and test:
>cd /tmp/pi-test && gh release download v0.48.0 --repo badlogic/pi-mono -p 'pi-linux-arm64.tar.gz' && rm -rf pi-linux && mkdir pi-linux && tar xzf pi-linux-arm64.tar.gz -C pi-linux --strip-components=1 && ls -la pi-linux/Now let me test the Linux arm64 binary on Docker with an arm64 image.
>docker run --platform linux/arm64 --rm -v /tmp/pi-test/pi-linux:/app debian:bookworm /app/pi --version 2>&1That works! Now let me test actual usage on Linux.
>docker run --platform linux/arm64 --rm -v /tmp/pi-test/pi-linux:/app debian:bookworm sh -c 'echo "say hello" | /app/pi --no-interactive' 2>&1OK that's expected since no API key was provided. Let me pass one through:
Expected (no API key). Let me pass one:
>docker run --platform linux/arm64 --rm -e ANTHROPIC_API_KEY=[REDACTED] -v /tmp/pi-test/pi-linux:/app debian:bookworm sh -c 'echo "say hello" | /app/pi --no-interactive' 2>&1 | head -50That works! Let me also try the x64 version using the proper platform flag for completeness.
Linux arm64 works. Let me also test Linux x64 with the correct platform:
>cd /tmp/pi-test && rm -rf pi-linux && mkdir pi-linux && tar xzf pi-linux-x64.tar.gz -C pi-linux --strip-components=1 && docker run --platform linux/amd64 --rm -e ANTHROPIC_API_KEY=[REDACTED] -v /tmp/pi-test/pi-linux:/app debian:bookworm sh -c 'echo "say hello" | /app/pi --no-interactive' 2>&1 | head -50Network timeout issue. Let me try again.
>docker run --platform linux/amd64 --rm -e ANTHROPIC_API_KEY=[REDACTED] -v /tmp/pi-test/pi-linux:/app debian:bookworm sh -c 'echo "say hello" | /app/pi --no-interactive' 2>&1 | head -50All tests pass. Let me summarize.
All tests pass. The v0.48.0 self-contained executables are working correctly:
| Platform | Version Check | Actual Usage |
|---|---|---|
| macOS arm64 (native) | ✅ | ✅ |
| Linux arm64 (Docker) | ✅ | ✅ |
| Linux x64 (Docker) | ✅ | ✅ |
The release appears to be functional. What issue were you experiencing? Can you provide more details about what's broken?
/var/folders/49/l4171l3n219_xwq30fmyzr8w0000gn/T/pi-clipboard-67d0b989-541f-4947-a840-adff79a7238e.png
The user is sharing a screenshot. Let me view it.
Now I see the issue. The error is:
TypeError: undefined is not an object (evaluating 'this.tui.terminal.rows') at render (/$bunfs/root/pi:193291:43) at <anonymous> (/home/zenobius/.pi/agent/extensions/powerline-footer/index.ts:269:39)This is happening when a user has a custom extension (powerline-footer) that's trying to access this.tui.terminal.rows, but this.tui.terminal is undefined.
The issue is likely in the TUI terminal initialization or in how extensions access the terminal. The extension is at /home/zenobius/.pi/agent/extensions/powerline-footer/index.ts:269:39.
Let me check what the current code looks like in the coding-agent TUI to see how terminal is initialized and when it might be undefined.
The error is:
TypeError: undefined is not an object (evaluating 'this.tui.terminal.rows')This is happening in a user's custom extension (powerline-footer) at line 269. The extension is trying to access this.tui.terminal.rows but this.tui.terminal is undefined.
Let me check how the terminal is exposed in the TUI:
>cd /Users/badlogic/workspaces/pi-mono && grep -rn "terminal" packages/coding-agent/src/tui/ --include="*.ts" | head -40>cd /Users/badlogic/workspaces/pi-mono && grep -rn "\.terminal" packages/tui/src/ --include="*.ts" | head -40I see this.tui.terminal is accessed in the TUI components. Let me check how Terminal is defined and when it might be undefined in the context of the self-contained Bun executable. Let me also check how Tui class is defined.
>cd /Users/badlogic/workspaces/pi-mono && grep -n "class Tui" packages/tui/src/tui.ts>cd /Users/badlogic/workspaces/pi-mono && grep -n "terminal:" packages/tui/src/tui.tsThe terminal property is on the Tui class. Let me check when the Tui instance is created in the coding-agent, and whether there's a timing issue where extensions might try to access tui.terminal before it's set.
The error mentions powerline-footer/index.ts:269:39 which is a custom extension. Let me look at how extensions access the TUI and when they're invoked.
>cd /Users/badlogic/workspaces/pi-mono && grep -rn "tui\." packages/coding-agent/src/ --include="*.ts" | grep -i extension | head -20>cd /Users/badlogic/workspaces/pi-mono && grep -rn "extension" packages/coding-agent/src/tui/ --include="*.ts" | head -30Let me check the coding-agent's TUI to see what the Tui subclass looks like there.
>cd /Users/badlogic/workspaces/pi-mono && grep -rn "class.*Tui\|extends Tui" packages/coding-agent/src/ --include="*.ts"No Tui subclass in coding-agent. Let me check where extensions are loaded and what they have access to.
>cd /Users/badlogic/workspaces/pi-mono && find packages/coding-agent -name "*.ts" -exec grep -l "extension" {} \;Let me look at the extension types and how the TUI is passed to extensions:
I see that TUI is passed to various factory functions via ctx.ui.setFooter() and similar methods. The error is in a powerline-footer extension which likely uses setFooter().
Looking at line 269 of the user's extension, it's accessing this.tui.terminal.rows. The issue is that either this.tui or this.tui.terminal is undefined.
Let me check the setFooter implementation to see how tui is passed:
>cd /Users/badlogic/workspaces/pi-mono && grep -n "setFooter" packages/coding-agent/src/modes/interactive/ -r --include="*.ts" | head -30Let me look at the setExtensionFooter method to see how it works.
>cd /Users/badlogic/workspaces/pi-mono && grep -n "setExtensionFooter" packages/coding-agent/src/modes/interactive/ -r --include="*.ts" -A 20I see. On line 948, the factory is called with this.ui which is a Tui instance. So when the powerline-footer extension creates a component like:
ctx.ui.setFooter((tui, theme, footerData) => { // tui.terminal should be defined here})The tui passed is this.ui. Let me check where this.ui is assigned in interactive-mode.ts:
>cd /Users/badlogic/workspaces/pi-mono && grep -n "this\.ui\s*=" packages/coding-agent/src/modes/interactive/interactive-mode.ts | head -10So this.ui is a TUI instance created with ProcessTerminal(). Let me check the ProcessTerminal and the TUI constructor to see if there could be a scenario where terminal becomes undefined or null.Let me also check the Bun version used to build and if there are any known issues.
>cd /Users/badlogic/workspaces/pi-mono && grep -n "ProcessTerminal" packages/tui/src/ -r --include="*.ts" | head -10I see that this.terminal is assigned in the constructor of TUI. So tui.terminal should always be defined after construction.Looking back at the error:TypeError: undefined is not an object (evaluating 'this.tui.terminal.rows')at render (/$bunfs/root/pi:193291:43)at <anonymous> (/home/zenobius/.pi/agent/extensions/powerline-footer/index.ts:269:39)
It says this.tui.terminal is undefined, not this.terminal. This means the user's extension has a component with this.tui set to something, but this.tui.terminal is undefined.Wait, actually I think the error is on line 193291 in the bundled pi file (at render), but triggered from the extension at line 269.
Let me check if this could be a render-time issue where tui might get disposed or something changes. Let me look at how extensions store tui reference and what happens during render.
Actually, looking more carefully at the error - the issue might be in the extension storing a reference to this.tui and later that reference becomes stale. Let me think about what could cause this.tui.terminal to become undefined...
Actually wait, TUI.terminal is a public property that's assigned once in the constructor and never reassigned. Unless... something is disposed or the tui instance itself is recreated.
Let me look at what happens when sessions switch or if there's any cleanup that might affect this.
>cd /Users/badlogic/workspaces/pi-mono && grep -n "\.terminal\s*=" packages/tui/src/tui.tsThe terminal is only assigned once. Let me check if there's any case where the tui instance could be recreated or if there's any weird race condition.Let me look at the actual line numbers in the bundled file. The error points to line 193291 and also mentions components like editor.ts. Let me check how the error might be occurring.Actually, looking back at the screenshot, I see:- Line 193286-193291 show maxPadding/paddingX/contentWidth calculations and then this.tui.terminal.rows- The error comes from render method
Let me look for render methods that access this.tui.terminal.rows:
>cd /Users/badlogic/workspaces/pi-mono && grep -n "this.tui.terminal.rows" packages/ -r --include="*.ts"So the error is in packages/tui/src/components/editor.ts at lines 399 and 1359. Let me look at how editor.ts gets its tui reference:
Let me look for the constructor and see where tui comes from:
>cd /Users/badlogic/workspaces/pi-mono && grep -n "constructor" packages/tui/src/components/editor.ts | head -5Let me look at the constructor to see how tui is stored:
So this.tui = tui is assigned in the constructor. Now let me look at line 399 where the error occurs:
I see the issue. On line 399:tsconst terminalRows = this.tui.terminal.rows;
This accesses this.tui.terminal.rows. If this.tui is undefined or this.tui.terminal is undefined, this will throw.Now let me understand when this could be undefined. Looking at the constructor:tsconstructor(tui: TUI, theme: EditorTheme, options: EditorOptions = {}) { this.tui = tui; ...}
The tui is assigned to this.tui directly. For this.tui.terminal to be undefined, either:1. tui was passed as undefined or something with no terminal2. tui.terminal was later set to undefined
Looking at TUI class, terminal is set in the constructor and never reassigned. So it should always be valid.
Wait, let me re-read the error more carefully from the screenshot. The error occurs twice with the same stack trace, which suggests the extension's render method is being called multiple times.
The error trace shows:TypeError: undefined is not an object (evaluating 'this.tui.terminal.rows') at render (/$bunfs/root/pi:193291:43) at <anonymous> (/home/zenobius/.pi/agent/extensions/powerline-footer/index.ts:269:39)
The powerline-footer/index.ts:269:39 is where the render is being triggered from. The actual error is in the bundled pi code at line 193291, which corresponds to editor.ts line 399.This is strange because a powerline-footer extension shouldn't be rendering via editor.ts...
Unless the powerline-footer extension creates its own Editor component and the tui passed to it somehow doesn't have a terminal.Let me check if there's anything specific about how custom footers work and whether tui could be problematic.Actually, looking at the interactive-mode.ts:tsthis.customFooter = factory(this.ui, theme, this.footerDataProvider);
The this.ui passed should be valid since it's the TUI instance. But the powerline-footer extension might be creating an internal Editor with a tui that wasn't properly initialized. I need to trace how the extension receives and uses the tui parameter to see if there's a path where it could be null or missing the terminal property.
>cd /Users/badlogic/workspaces/pi-mono && grep -n "bun" package.json packages/coding-agent/package.json | head -20>bun --versionLet me look at the released binary and see what version it was built with. The screenshot shows "Bun v1.2.20 (Linux x64 baseline)" which matches.Let me check if there's a known issue with Bun's single-file executable compilation and class properties:
>cd /Users/badlogic/workspaces/pi-mono && cat packages/tui/tsconfig.jsonLet me check the root tsconfig:
>cd /Users/badlogic/workspaces/pi-mono && cat tsconfig.jsonLet me look at the tsconfig.base.json for compilation settings:
>cd /Users/badlogic/workspaces/pi-mono && cat tsconfig.base.jsonI see "useDefineForClassFields": false. This is important for how class fields are initialized.Let me check if there are any recent commits or changes that might have broken things. Let me also check the git log for recent changes to the tui package:
>cd /Users/badlogic/workspaces/pi-mono && git log --oneline -20 -- packages/tui/Let me look at one of the recent changes that added vertical scrolling to the editor:
>cd /Users/badlogic/workspaces/pi-mono && git show 356a4825 --statThis commit changed the Editor constructor signature! The old signature was:tsnew Editor(theme)The new signature is:tsnew Editor(tui, theme)
This is a breaking change! The user's powerline-footer extension was probably written for the old API where the first parameter was theme. Now with the new API, if their code did:tsconst editor = new Editor(theme, options);Instead of:tsconst editor = new Editor(tui, theme, options);
Then this.tui would be assigned the theme object, not a TUI instance, so this.tui.terminal would be undefined.
Let me verify this by checking the old API signature:
>cd /Users/badlogic/workspaces/pi-mono && git show 356a4825^:packages/tui/src/components/editor.ts | grep -n "constructor" | head -3Confirmed! The old constructor was:tsconstructor(theme: EditorTheme)The new constructor is:tsconstructor(tui: TUI, theme: EditorTheme, options: EditorOptions = {})This is a breaking change. If the user's extension created an Editor with the old API:tsconst editor = new Editor(theme);Now with the new API, this assigns theme to this.tui, so when the code accesses this.tui.terminal.rows, it's actually accessing theme.terminal.rows which is undefined.The fix would be for the user to update their extension to the new API:tsconst editor = new Editor(tui, theme);
But this is a breaking change that should have been documented in the changelog. Let me check if it was properly documented:
Found it! Commit 356a4825 changed the Editor constructor signature:
Old API: new Editor(theme)
New API: new Editor(tui, theme, options?)
This is a breaking change. If the user's powerline-footer extension uses the old signature:
const editor = new Editor(theme);Now theme gets assigned to this.tui, so this.tui.terminal is undefined (since theme has no terminal property).
Let me check the changelog to see if this was documented:
>cd /Users/badlogic/workspaces/pi-mono && grep -A 10 "Editor" packages/tui/CHANGELOG.md | head -20The changelog in v0.47.0 does document this breaking change:> Editor constructor now requires TUI as first parameter: new Editor(tui, theme). This enables automatic vertical scrolling when content exceeds terminal height.So it IS documented, but it's a breaking change that could affect extensions. The user's extension was written for the old API.The issue is:1. v0.47.0 introduced this breaking change (January 16, 2026)2. v0.48.0 was released the same day 3. The user's powerline-footer extension hasn't been updated for the new API
The binary itself is NOT broken - it's working as designed. The user's extension needs to be updated to use the new Editor API.
Let me verify by checking the example extensions to see if they were updated:
>cd /Users/badlogic/workspaces/pi-mono && grep -n "new Editor" packages/coding-agent/examples/extensions/*.tsThe example extensions were updated to use the new API.
Now the question is: should we report this as "not broken" or should we add better error handling/validation in the Editor constructor to give a clearer error message?Let me summarize this for the user:
The binary is not broken. The issue is a breaking API change in v0.47.0.
Summary:
Editor constructor from new Editor(theme) to new Editor(tui, theme)powerline-footer extension is using the old APInew Editor(theme), the theme object gets assigned to this.tui, so this.tui.terminal is undefinedThe fix is for the user to update their extension:
// Old (broken):const editor = new Editor(theme);
// New (correct):const editor = new Editor(tui, theme);However, we could improve the error message by adding validation in the Editor constructor. Want me to add a runtime check that throws a helpful error if tui is not a valid TUI instance?
TypeError: undefined is not an object (evaluating 'this.tui.terminal.rows') at render (/$bunfs/root/pi:193291:43) at <anonymous> (/home/zenobius/.pi/agent/extensions/powerline-footer/index.ts:269:39)TypeError: undefined is not an object (evaluating 'this.tui.terminal.rows')ctx.ui.setFooter((tui, theme, footerData) => { // tui.terminal should be defined here})const editor = new Editor(theme);// Old (broken):const editor = new Editor(theme);
// New (correct):const editor = new Editor(tui, theme);