[GH-ISSUE #405] OpenTUI zombie process #871

Closed
opened 2026-03-14 08:52:19 +03:00 by kerem · 10 comments
Owner

Originally created by @imaai on GitHub (Dec 11, 2025).
Original GitHub issue: https://github.com/anomalyco/opentui/issues/405

Originally assigned to: @msmps on GitHub.

OpenTUI application doesn't close properly leaving zombie processes in the background. Reproduced on my machine with bun create tui and then bun dev. Also this happens regardless of --watch flag.

https://github.com/user-attachments/assets/20aa1598-b638-427c-923b-a2bfc9ad368a

Originally created by @imaai on GitHub (Dec 11, 2025). Original GitHub issue: https://github.com/anomalyco/opentui/issues/405 Originally assigned to: @msmps on GitHub. OpenTUI application doesn't close properly leaving zombie processes in the background. Reproduced on my machine with `bun create tui` and then `bun dev`. Also this happens regardless of `--watch` flag. https://github.com/user-attachments/assets/20aa1598-b638-427c-923b-a2bfc9ad368a
kerem closed this issue 2026-03-14 08:52:24 +03:00
Author
Owner

@kommander commented on GitHub (Dec 11, 2025):

The lifecycle behavior recently changed, the bunx create tui templates might be out of date. The app must call renderer.destroy for it to shutdown properly.

<!-- gh-comment-id:3641638482 --> @kommander commented on GitHub (Dec 11, 2025): The lifecycle behavior recently changed, the `bunx create tui` templates might be out of date. The app must call `renderer.destroy` for it to shutdown properly.
Author
Owner

@imaai commented on GitHub (Dec 11, 2025):

@kommander Would you be able to provide a working example because I have already tried that and it doesn't seem to resolve an issue. I checked previous versions down to 0.1.50 on my machine and I always have this issue.

Should this work with such configuration out of the box?

const renderer = await createCliRenderer({
	exitOnCtrlC: true,
});

createRoot(renderer).render(<App />);
<!-- gh-comment-id:3642073970 --> @imaai commented on GitHub (Dec 11, 2025): @kommander Would you be able to provide a working example because I have already tried that and it doesn't seem to resolve an issue. I checked previous versions down to 0.1.50 on my machine and I always have this issue. Should this work with such configuration out of the box? ``` const renderer = await createCliRenderer({ exitOnCtrlC: true, }); createRoot(renderer).render(<App />); ```
Author
Owner

@imaai commented on GitHub (Dec 11, 2025):

The app must call renderer.destroy for it to shutdown properly.

Even when triggering destroy manually it leaves dead process.


export const Application = () => {
	const renderer = useRenderer();

	useEffect(() => {
		setTimeout(() => {
			renderer.destroy();
		}, 1000);
	}, [renderer]);

	return (
		<box width="100%" height="100%" backgroundColor="#000000">
			<box style={{ flexGrow: 1, backgroundColor: "red" }} />
		</box>
	);
};

https://github.com/user-attachments/assets/b9c64f11-51eb-4725-acb7-e73f8d4d6e2d

<!-- gh-comment-id:3642079898 --> @imaai commented on GitHub (Dec 11, 2025): > The app must call renderer.destroy for it to shutdown properly. Even when triggering destroy manually it leaves dead process. ``` export const Application = () => { const renderer = useRenderer(); useEffect(() => { setTimeout(() => { renderer.destroy(); }, 1000); }, [renderer]); return ( <box width="100%" height="100%" backgroundColor="#000000"> <box style={{ flexGrow: 1, backgroundColor: "red" }} /> </box> ); }; ``` https://github.com/user-attachments/assets/b9c64f11-51eb-4725-acb7-e73f8d4d6e2d
Author
Owner

@imaai commented on GitHub (Dec 11, 2025):

@kommander Okay this one resolved that. Is it the desired way?


const renderer = await createCliRenderer({
	onDestroy: () => {
		process.exit(0);
	},
});

createRoot(renderer).render(<App />);
<!-- gh-comment-id:3642212899 --> @imaai commented on GitHub (Dec 11, 2025): @kommander Okay this one resolved that. Is it the desired way? ``` const renderer = await createCliRenderer({ onDestroy: () => { process.exit(0); }, }); createRoot(renderer).render(<App />); ```
Author
Owner

@kommander commented on GitHub (Dec 11, 2025):

The process should shut down on its own if there is nothing keeping it alive. Is it react or solid you are using? Might be something still has an event listener somewhere. What OS are you on?

<!-- gh-comment-id:3642300084 --> @kommander commented on GitHub (Dec 11, 2025): The process should shut down on its own if there is nothing keeping it alive. Is it react or solid you are using? Might be something still has an event listener somewhere. What OS are you on?
Author
Owner

@imaai commented on GitHub (Dec 11, 2025):

I'm running react on macOS. and this is litreally everything in this test package

import { createCliRenderer } from "@opentui/core";
import { createRoot } from "@opentui/react";
import { Application } from "./app";

const App = () => {
        return <Application />;
};

const renderer = await createCliRenderer({
        onDestroy: () => {
                process.exit(0);
        },
});

createRoot(renderer).render(<App />);
import { useRenderer } from "@opentui/react";

export const Application = () => {
        const _renderer = useRenderer();

        return (
                <box width="100%" height="100%" backgroundColor="#000000">
                        <box style={{ flexGrow: 1, backgroundColor: "red" }} />
                </box>
        );
};
<!-- gh-comment-id:3642310998 --> @imaai commented on GitHub (Dec 11, 2025): I'm running react on macOS. and this is litreally everything in this test package ``` import { createCliRenderer } from "@opentui/core"; import { createRoot } from "@opentui/react"; import { Application } from "./app"; const App = () => { return <Application />; }; const renderer = await createCliRenderer({ onDestroy: () => { process.exit(0); }, }); createRoot(renderer).render(<App />); ``` ``` import { useRenderer } from "@opentui/react"; export const Application = () => { const _renderer = useRenderer(); return ( <box width="100%" height="100%" backgroundColor="#000000"> <box style={{ flexGrow: 1, backgroundColor: "red" }} /> </box> ); }; ```
Author
Owner

@kommander commented on GitHub (Dec 11, 2025):

@msmps do you have an idea if something needs to change for react maybe? I can't reproduce the issue and opencode shuts down fine as well.

<!-- gh-comment-id:3642516604 --> @kommander commented on GitHub (Dec 11, 2025): @msmps do you have an idea if something needs to change for react maybe? I can't reproduce the issue and opencode shuts down fine as well.
Author
Owner

@msmps commented on GitHub (Dec 12, 2025):

@kommander had a very quick look into this (on vacation) but it doesn't look specific to react... it's also happening for solid more so when using bun run --watch 🤔

  1. bun create tui --template solid repro
  2. bun run dev or bun run --watch src/index.tsx
<!-- gh-comment-id:3648118144 --> @msmps commented on GitHub (Dec 12, 2025): @kommander had a very quick look into this (_on vacation_) but it doesn't look specific to react... it's also happening for solid more so when using `bun run --watch` 🤔 1. `bun create tui --template solid repro` 2. `bun run dev` or `bun run --watch src/index.tsx`
Author
Owner

@kommander commented on GitHub (Dec 13, 2025):

--watch never really worked afaik. I'll check the shutdown behavior.

<!-- gh-comment-id:3649522318 --> @kommander commented on GitHub (Dec 13, 2025): `--watch` never really worked afaik. I'll check the shutdown behavior.
Author
Owner

@remorses commented on GitHub (Dec 14, 2025):

Fixed in https://github.com/sst/opentui/pull/383

<!-- gh-comment-id:3651742826 --> @remorses commented on GitHub (Dec 14, 2025): Fixed in https://github.com/sst/opentui/pull/383
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/opentui#871
No description provided.