charmbracelet/crush · error

error starting background shell: %w

Error message

error starting background shell: %w

What it means

When params.RunInBackground is set, the bash tool starts the command via the background shell manager (bgManager.Start) on a detached context.Background so it survives the tool call. If Start returns an error — the process could not be spawned — the tool fails with 'error starting background shell' wrapping the underlying cause.

Source

Thrown at internal/agent/tools/bash.go:256

					},
				)
				if err != nil {
					return fantasy.ToolResponse{}, err
				}
				if !p {
					return NewPermissionDeniedResponse(), nil
				}
			}

			// If explicitly requested as background, start immediately with detached context
			if params.RunInBackground {
				startTime := time.Now()
				bgManager := shell.GetBackgroundShellManager()
				bgManager.Cleanup()
				// Use background context so it continues after tool returns
				bgShell, err := bgManager.Start(context.Background(), execWorkingDir, blockFuncs(), params.Command, params.Description)
				if err != nil {
					return fantasy.ToolResponse{}, fmt.Errorf("error starting background shell: %w", err)
				}

				// Wait a short time to detect fast failures (blocked commands, syntax errors, etc.)
				time.Sleep(1 * time.Second)
				stdout, stderr, done, execErr := bgShell.GetOutput()

				if done {
					// Command failed or completed very quickly
					bgManager.Remove(bgShell.ID)

					interrupted := shell.IsInterrupt(execErr)
					exitCode := shell.ExitCode(execErr)
					if exitCode == 0 && !interrupted && execErr != nil {
						return fantasy.ToolResponse{}, fmt.Errorf("[Job %s] error executing command: %w", bgShell.ID, execErr)
					}

					stdout = formatOutput(stdout, stderr, execErr)

View on GitHub (pinned to 7944b8e522)

Solutions

  1. Verify the working directory passed in params.WorkingDir (or the tool's configured workingDir) exists and is readable/executable
  2. Confirm a shell is available on the host/container PATH and process spawning is permitted (ulimit, seccomp, sandbox policy)
  3. Check the wrapped cause (%w) for the OS-level reason (e.g. 'no such file or directory' vs 'fork: resource temporarily unavailable') and fix accordingly
  4. Restart the app if the background shell manager is in a wedged state after repeated failures

Example fix

// before
Bash(command="ls", working_dir="/gone/path", run_in_background=true)
// after
Bash(command="ls", working_dir="/existing/project", run_in_background=true)
Defensive patterns

Strategy: validation

Validate before calling

if info, err := os.Stat(workingDir); err != nil || !info.IsDir() {
    return fmt.Errorf("working dir %q is not usable: %w", workingDir, err)
}
if _, err := exec.LookPath("bash"); err != nil {
    return fmt.Errorf("shell unavailable: %w", err)
}

Try / catch

shell, err := bgManager.Start(ctx, dir, blockers, cmd, desc)
if err != nil {
    var perr *exec.Error
    if errors.As(err, &perr) { /* missing binary/PATH problem */ }
    return fmt.Errorf("background shell unavailable: %w", err)
}

Prevention

When it happens

Trigger: bgManager.Start fails while handling a RunInBackground=true bash call: the working directory (params.WorkingDir or the tool's workingDir) does not exist or is not accessible, the shell executable is missing, OS fork/exec limits are hit, or StartPersistent/Start fails internally.

Common situations: The model passes a WorkingDir that was deleted or never existed; running in a sandboxed container without a shell on PATH; hitting process/file-descriptor limits after many leaked background jobs.

Related errors


AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29). Data as JSON: /api/errors/817c14b7dc8e04e1. Report an issue: GitHub.