fix(core): ensure robust sandbox cleanup in all process execution paths (#24763)

Co-authored-by: Spencer <spencertang@google.com>
This commit is contained in:
Emily Hedlund
2026-04-08 12:03:36 -07:00
committed by GitHub
parent 3df99d8bcb
commit a39461718c
5 changed files with 412 additions and 362 deletions
@@ -59,52 +59,56 @@ export class SandboxedFileSystemService implements FileSystemService {
},
});
return new Promise((resolve, reject) => {
// Direct spawn is necessary here for streaming large file contents.
try {
return await new Promise((resolve, reject) => {
// Direct spawn is necessary here for streaming large file contents.
const child = spawn(prepared.program, prepared.args, {
cwd: this.cwd,
env: prepared.env,
});
const child = spawn(prepared.program, prepared.args, {
cwd: this.cwd,
env: prepared.env,
});
let output = '';
let error = '';
let output = '';
let error = '';
child.stdout?.on('data', (data) => {
output += data.toString();
});
child.stdout?.on('data', (data) => {
output += data.toString();
});
child.stderr?.on('data', (data) => {
error += data.toString();
});
child.stderr?.on('data', (data) => {
error += data.toString();
});
child.on('close', (code) => {
if (code === 0) {
resolve(output);
} else {
const isEnoent =
error.toLowerCase().includes('no such file or directory') ||
error.toLowerCase().includes('enoent') ||
error.toLowerCase().includes('could not find file') ||
error.toLowerCase().includes('could not find a part of the path');
const err = new Error(
`Sandbox Error: read_file failed for '${filePath}'. Exit code ${code}. ${error ? 'Details: ' + error : ''}`,
);
if (isEnoent) {
Object.assign(err, { code: 'ENOENT' });
child.on('close', (code) => {
if (code === 0) {
resolve(output);
} else {
const isEnoent =
error.toLowerCase().includes('no such file or directory') ||
error.toLowerCase().includes('enoent') ||
error.toLowerCase().includes('could not find file') ||
error.toLowerCase().includes('could not find a part of the path');
const err = new Error(
`Sandbox Error: read_file failed for '${filePath}'. Exit code ${code}. ${error ? 'Details: ' + error : ''}`,
);
if (isEnoent) {
Object.assign(err, { code: 'ENOENT' });
}
reject(err);
}
reject(err);
}
});
});
child.on('error', (err) => {
reject(
new Error(
`Sandbox Error: Failed to spawn read_file for '${filePath}': ${err.message}`,
),
);
child.on('error', (err) => {
reject(
new Error(
`Sandbox Error: Failed to spawn read_file for '${filePath}': ${err.message}`,
),
);
});
});
});
} finally {
prepared.cleanup?.();
}
}
async writeTextFile(filePath: string, content: string): Promise<void> {
@@ -124,53 +128,57 @@ export class SandboxedFileSystemService implements FileSystemService {
},
});
return new Promise((resolve, reject) => {
// Direct spawn is necessary here for streaming large file contents.
try {
return await new Promise((resolve, reject) => {
// Direct spawn is necessary here for streaming large file contents.
const child = spawn(prepared.program, prepared.args, {
cwd: this.cwd,
env: prepared.env,
});
const child = spawn(prepared.program, prepared.args, {
cwd: this.cwd,
env: prepared.env,
});
child.stdin?.on('error', (err) => {
// Silently ignore EPIPE errors on stdin, they will be caught by the process error/close listeners
if (isNodeError(err) && err.code === 'EPIPE') {
return;
}
debugLogger.error(
`Sandbox Error: stdin error for '${filePath}': ${
err instanceof Error ? err.message : String(err)
}`,
);
});
child.stdin?.on('error', (err) => {
// Silently ignore EPIPE errors on stdin, they will be caught by the process error/close listeners
if (isNodeError(err) && err.code === 'EPIPE') {
return;
}
debugLogger.error(
`Sandbox Error: stdin error for '${filePath}': ${
err instanceof Error ? err.message : String(err)
}`,
);
});
child.stdin?.write(content);
child.stdin?.end();
child.stdin?.write(content);
child.stdin?.end();
let error = '';
child.stderr?.on('data', (data) => {
error += data.toString();
});
let error = '';
child.stderr?.on('data', (data) => {
error += data.toString();
});
child.on('close', (code) => {
if (code === 0) {
resolve();
} else {
child.on('close', (code) => {
if (code === 0) {
resolve();
} else {
reject(
new Error(
`Sandbox Error: write_file failed for '${filePath}'. Exit code ${code}. ${error ? 'Details: ' + error : ''}`,
),
);
}
});
child.on('error', (err) => {
reject(
new Error(
`Sandbox Error: write_file failed for '${filePath}'. Exit code ${code}. ${error ? 'Details: ' + error : ''}`,
`Sandbox Error: Failed to spawn write_file for '${filePath}': ${err.message}`,
),
);
}
});
});
child.on('error', (err) => {
reject(
new Error(
`Sandbox Error: Failed to spawn write_file for '${filePath}': ${err.message}`,
),
);
});
});
} finally {
prepared.cleanup?.();
}
}
}