mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-10 11:24:58 +00:00
Fix: Prevent file descriptor leaks in child process cleanup (#13565)
* fix: prevent FD leaks in child process cleanup - Destroy stdio streams (stdin/stdout/stderr) after process exit - Remove event listeners to prevent memory leaks - Clean up child process reference in moveToFinished() - Also fixes model override handling in agent.ts Fixes EBADF errors caused by accumulating file descriptors from sub-agent spawns. * Fix: allow stdin destroy in process registry cleanup --------- Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
This commit is contained in:
@@ -20,6 +20,8 @@ export type ProcessStatus = "running" | "completed" | "failed" | "killed";
|
|||||||
export type SessionStdin = {
|
export type SessionStdin = {
|
||||||
write: (data: string, cb?: (err?: Error | null) => void) => void;
|
write: (data: string, cb?: (err?: Error | null) => void) => void;
|
||||||
end: () => void;
|
end: () => void;
|
||||||
|
// When backed by a real Node stream (child.stdin), this exists; for PTY wrappers it may not.
|
||||||
|
destroy?: () => void;
|
||||||
destroyed?: boolean;
|
destroyed?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -157,6 +159,38 @@ export function markBackgrounded(session: ProcessSession) {
|
|||||||
|
|
||||||
function moveToFinished(session: ProcessSession, status: ProcessStatus) {
|
function moveToFinished(session: ProcessSession, status: ProcessStatus) {
|
||||||
runningSessions.delete(session.id);
|
runningSessions.delete(session.id);
|
||||||
|
|
||||||
|
// Clean up child process stdio streams to prevent FD leaks
|
||||||
|
if (session.child) {
|
||||||
|
// Destroy stdio streams to release file descriptors
|
||||||
|
session.child.stdin?.destroy?.();
|
||||||
|
session.child.stdout?.destroy?.();
|
||||||
|
session.child.stderr?.destroy?.();
|
||||||
|
|
||||||
|
// Remove all event listeners to prevent memory leaks
|
||||||
|
session.child.removeAllListeners();
|
||||||
|
|
||||||
|
// Clear the reference
|
||||||
|
delete session.child;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up stdin wrapper - call destroy if available, otherwise just remove reference
|
||||||
|
if (session.stdin) {
|
||||||
|
// Try to call destroy/end method if exists
|
||||||
|
if (typeof session.stdin.destroy === "function") {
|
||||||
|
session.stdin.destroy();
|
||||||
|
} else if (typeof session.stdin.end === "function") {
|
||||||
|
session.stdin.end();
|
||||||
|
}
|
||||||
|
// Only set flag if writable
|
||||||
|
try {
|
||||||
|
(session.stdin as { destroyed?: boolean }).destroyed = true;
|
||||||
|
} catch {
|
||||||
|
// Ignore if read-only
|
||||||
|
}
|
||||||
|
delete session.stdin;
|
||||||
|
}
|
||||||
|
|
||||||
if (!session.backgrounded) {
|
if (!session.backgrounded) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user