{"record":{"id":"817c14b7dc8e04e1","repo":"charmbracelet/crush","slug":"error-starting-background-shell-w","errorCode":null,"errorMessage":"error starting background shell: %w","messagePattern":"error starting background shell: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/agent/tools/bash.go","lineNumber":256,"sourceCode":"\t\t\t\t\t},\n\t\t\t\t)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn fantasy.ToolResponse{}, err\n\t\t\t\t}\n\t\t\t\tif !p {\n\t\t\t\t\treturn NewPermissionDeniedResponse(), nil\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// If explicitly requested as background, start immediately with detached context\n\t\t\tif params.RunInBackground {\n\t\t\t\tstartTime := time.Now()\n\t\t\t\tbgManager := shell.GetBackgroundShellManager()\n\t\t\t\tbgManager.Cleanup()\n\t\t\t\t// Use background context so it continues after tool returns\n\t\t\t\tbgShell, err := bgManager.Start(context.Background(), execWorkingDir, blockFuncs(), params.Command, params.Description)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn fantasy.ToolResponse{}, fmt.Errorf(\"error starting background shell: %w\", err)\n\t\t\t\t}\n\n\t\t\t\t// Wait a short time to detect fast failures (blocked commands, syntax errors, etc.)\n\t\t\t\ttime.Sleep(1 * time.Second)\n\t\t\t\tstdout, stderr, done, execErr := bgShell.GetOutput()\n\n\t\t\t\tif done {\n\t\t\t\t\t// Command failed or completed very quickly\n\t\t\t\t\tbgManager.Remove(bgShell.ID)\n\n\t\t\t\t\tinterrupted := shell.IsInterrupt(execErr)\n\t\t\t\t\texitCode := shell.ExitCode(execErr)\n\t\t\t\t\tif exitCode == 0 && !interrupted && execErr != nil {\n\t\t\t\t\t\treturn fantasy.ToolResponse{}, fmt.Errorf(\"[Job %s] error executing command: %w\", bgShell.ID, execErr)\n\t\t\t\t\t}\n\n\t\t\t\t\tstdout = formatOutput(stdout, stderr, execErr)\n","sourceCodeStart":238,"sourceCodeEnd":274,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/agent/tools/bash.go#L238-L274","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Verify the working directory passed in params.WorkingDir (or the tool's configured workingDir) exists and is readable/executable","Confirm a shell is available on the host/container PATH and process spawning is permitted (ulimit, seccomp, sandbox policy)","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","Restart the app if the background shell manager is in a wedged state after repeated failures"],"exampleFix":"// before\nBash(command=\"ls\", working_dir=\"/gone/path\", run_in_background=true)\n// after\nBash(command=\"ls\", working_dir=\"/existing/project\", run_in_background=true)","handlingStrategy":"validation","validationCode":"if info, err := os.Stat(workingDir); err != nil || !info.IsDir() {\n    return fmt.Errorf(\"working dir %q is not usable: %w\", workingDir, err)\n}\nif _, err := exec.LookPath(\"bash\"); err != nil {\n    return fmt.Errorf(\"shell unavailable: %w\", err)\n}","typeGuard":null,"tryCatchPattern":"shell, err := bgManager.Start(ctx, dir, blockers, cmd, desc)\nif err != nil {\n    var perr *exec.Error\n    if errors.As(err, &perr) { /* missing binary/PATH problem */ }\n    return fmt.Errorf(\"background shell unavailable: %w\", err)\n}","preventionTips":["Validate WorkingDir exists before passing it in Bash tool params","Run in environments where the shell is on PATH and fork/exec is permitted","Monitor ulimit/fd usage if spawning many background jobs"],"tags":["go","shell","background-process"],"backgroundTag":"process-start-failed","analyzedSha":"7944b8e52225d8805e31eacbf7ef24856b0dfb7a","analyzedAt":"2026-08-29T12:48:59.079Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}