gemini-cli - 💡(How to fix) Fix CLI Crash on Tool Completion (ioctl(2) failed, EBADF) [1 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
google-gemini/gemini-cli#26433Fetched 2026-05-05 06:03:45
View on GitHub
Comments
1
Participants
2
Timeline
2
Reactions
0
Author
Timeline (top)
commented ×1labeled ×1

The CLI crashes with Error: ioctl(2) failed, EBADF immediately after a shell tool finishes execution. This occurs because a React-style UI update triggers a resizePty() call on a Pseudo-Terminal (PTY) whose file descriptor has already been closed by the tool's termination.

Error Message

The CLI crashes with Error: ioctl(2) failed, EBADF immediately after a shell tool finishes execution. This occurs because a React-style UI update triggers a resizePty() call on a Pseudo-Terminal (PTY) whose file descriptor has already been closed by the tool's termination. 3. The CLI will crash with a red error block the moment the tool finishes and the TUI attempts to render the success state. ERROR ioctl(2) failed, EBADF 2. The Catch Failure: The native error thrown by the node-pty C++ binding when using ioctl on an invalid descriptor does not have a .code property (e.g., it is undefined, not "EBADF"). 3. The Result: The CLI's internal error handling likely checks for err.code === 'ESRCH' but misses this error because it only contains a .message string ("ioctl(2) failed, EBADF"). The unhandled exception then crashes the entire process. Update the resizePty wrapper in the CLI bundle to perform a string-based check on the error message when .code is missing:

Root Cause

The crash is a race condition between the PTY teardown and the UI layout engine:

  1. The Race: A tool finishes -> PTY is destroyed -> UI update triggers -> resizePty() is called.
  2. The Catch Failure: The native error thrown by the node-pty C++ binding when using ioctl on an invalid descriptor does not have a .code property (e.g., it is undefined, not "EBADF").
  3. The Result: The CLI's internal error handling likely checks for err.code === 'ESRCH' but misses this error because it only contains a .message string ("ioctl(2) failed, EBADF"). The unhandled exception then crashes the entire process.

Code Example

bunx --bun @google/gemini-cli@latest

---

ps aux | head -n 20

---

ERROR  ioctl(2) failed, EBADF

 -     at unknown
 -<anonymous> (/tmp/bunx-1000-@google/gemini-cli@latest/node_modules/@lydell/node-pty/unixTerminal.js:243:13)
 -resizePty (/tmp/bunx-1000-@google/gemini-cli@latest/node_modules/@google/gemini-cli/bundle/chunk-UN6XCVMJ.js:285678:30)
 -<anonymous> (/tmp/bunx-1000-@google/gemini-cli@latest/node_modules/@google/gemini-cli/bundle/interactiveCli-VO2T47FA.js:7102:31)
 -commitHookEffectListMount (/tmp/bunx-1000-@google/gemini-cli@latest/node_modules/@google/gemini-cli/bundle/chunk-VWLKCG5Q.js:5252:30)

---

try {
  this.pty.resize(cols, rows);
} catch (err) {
  // node-pty native errors often lack .code; check message for EBADF
  if (err.code === 'ESRCH' || err.message?.includes('EBADF')) {
    return; // Safe to ignore during teardown
  }
  throw err;
}
RAW_BUFFERClick to expand / collapse

What happened?

CLI Version: 0.40.1 (via @latest) Runtime: Bun (invoked with bunx --bun) Operating System: Linux

Summary

The CLI crashes with Error: ioctl(2) failed, EBADF immediately after a shell tool finishes execution. This occurs because a React-style UI update triggers a resizePty() call on a Pseudo-Terminal (PTY) whose file descriptor has already been closed by the tool's termination.

Reproduction Steps

  1. Launch the CLI using the Bun runtime:
    bunx --bun @google/gemini-cli@latest
  2. Execute a command that terminates quickly or uses a pipe to force an early exit:
    ps aux | head -n 20
  3. The CLI will crash with a red error block the moment the tool finishes and the TUI attempts to render the success state.

Backtrace

ERROR  ioctl(2) failed, EBADF

 -     at unknown
 -<anonymous> (/tmp/bunx-1000-@google/gemini-cli@latest/node_modules/@lydell/node-pty/unixTerminal.js:243:13)
 -resizePty (/tmp/bunx-1000-@google/gemini-cli@latest/node_modules/@google/gemini-cli/bundle/chunk-UN6XCVMJ.js:285678:30)
 -<anonymous> (/tmp/bunx-1000-@google/gemini-cli@latest/node_modules/@google/gemini-cli/bundle/interactiveCli-VO2T47FA.js:7102:31)
 -commitHookEffectListMount (/tmp/bunx-1000-@google/gemini-cli@latest/node_modules/@google/gemini-cli/bundle/chunk-VWLKCG5Q.js:5252:30)

Related Issues

Root Cause Analysis

The crash is a race condition between the PTY teardown and the UI layout engine:

  1. The Race: A tool finishes -> PTY is destroyed -> UI update triggers -> resizePty() is called.
  2. The Catch Failure: The native error thrown by the node-pty C++ binding when using ioctl on an invalid descriptor does not have a .code property (e.g., it is undefined, not "EBADF").
  3. The Result: The CLI's internal error handling likely checks for err.code === 'ESRCH' but misses this error because it only contains a .message string ("ioctl(2) failed, EBADF"). The unhandled exception then crashes the entire process.

Suggested Fix

Update the resizePty wrapper in the CLI bundle to perform a string-based check on the error message when .code is missing:

try {
  this.pty.resize(cols, rows);
} catch (err) {
  // node-pty native errors often lack .code; check message for EBADF
  if (err.code === 'ESRCH' || err.message?.includes('EBADF')) {
    return; // Safe to ignore during teardown
  }
  throw err;
}

What did you expect to happen?

CLI does not crash

Client information

  • CLI Version: 0.40.1
  • Git Commit: 7a382e066
  • Operating System: linux v24.3.0
  • Sandbox Environment: no sandbox
  • Model Version: auto-gemini-3
  • Auth Type: oauth-personal
  • Memory Usage: 377.4 MB
  • Terminal Name: VTE(8203)
  • Terminal Background: #1c1c1f
  • Kitty Keyboard Protocol: Unsupported

Login information

No response

Anything else we need to know?

No response

extent analysis

TL;DR

Update the resizePty wrapper to catch and ignore errors with a message containing "EBADF" when the .code property is missing.

Guidance

  • Check the error handling in the resizePty function to ensure it accounts for native errors without a .code property.
  • Verify that the node-pty version is compatible with the current Linux environment.
  • Consider adding a try-catch block to safely ignore errors during PTY teardown.
  • Review related issues (#10523, #10617, and oven-sh/bun #27285) for potential workarounds or fixes.

Example

try {
  this.pty.resize(cols, rows);
} catch (err) {
  if (err.code === 'ESRCH' || err.message?.includes('EBADF')) {
    return; // Safe to ignore during teardown
  }
  throw err;
}

Notes

This fix assumes that the error message will always contain "EBADF" when the .code property is missing. Additional error handling may be necessary to account for other potential error scenarios.

Recommendation

Apply the suggested fix to update the resizePty wrapper, as it provides a targeted solution to the identified issue.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

gemini-cli - 💡(How to fix) Fix CLI Crash on Tool Completion (ioctl(2) failed, EBADF) [1 comments, 2 participants]