Skip to content

Commit aaf26a8

Browse files
🤖 fix: prevent background process name collisions (#1070)
## Problem Background processes are stored in a `Map<processId, BackgroundProcess>` in `BackgroundProcessManager`. The `processId` was set directly from `displayName`, and it's also used to compute the output directory: ```typescript const processId = config.displayName; // ... const outputDir = `${bgOutputDir}/${workspaceId}/${processId}`; ``` When a new process is spawned in the background with a display name that was previously used: 1. It gets the **same `outputDir`** as the old process 2. The old `exit_code` file still exists in that directory (written by shell trap on exit) 3. When `refreshRunningStatuses()` checks the new process, it reads the **stale `exit_code` file** 4. The new process is immediately marked as `"exited"` even though it just started 5. The banner filters to `status === "running"` → **process doesn't appear** Additionally, the map entry itself gets overwritten, losing tracking of any previous process with that name. ## Fix Append incrementing suffix `(1)`, `(2)`, etc. when a collision is detected: ```typescript let processId = config.displayName; let suffix = 1; while (this.processes.has(processId)) { processId = `${config.displayName} (${suffix})`; suffix++; } ``` This ensures each process gets a unique `outputDir`, so no stale `exit_code` files interfere. The original `displayName` is preserved separately on the `BackgroundProcess` object for UI display - the banner shows clean names like "Dev Server" while the agent receives unique IDs like "Dev Server (1)" to reference specific processes. _Generated with `mux`_
1 parent 34895ca commit aaf26a8

File tree

1 file changed

+7
-2
lines changed

1 file changed

+7
-2
lines changed

src/node/services/backgroundProcessManager.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -170,8 +170,13 @@ export class BackgroundProcessManager extends EventEmitter<BackgroundProcessMana
170170
> {
171171
log.debug(`BackgroundProcessManager.spawn() called for workspace ${workspaceId}`);
172172

173-
// Process ID is the display name directly
174-
const processId = config.displayName;
173+
// Generate unique processId, appending (1), (2), etc. on collision
174+
let processId = config.displayName;
175+
let suffix = 1;
176+
while (this.processes.has(processId)) {
177+
processId = `${config.displayName} (${suffix})`;
178+
suffix++;
179+
}
175180

176181
// Spawn via executor with background infrastructure
177182
// spawnProcess uses runtime.tempDir() internally for output directory

0 commit comments

Comments
 (0)