{"record":{"id":"d406a0d44a060cf3","repo":"charmbracelet/crush","slug":"error-starting-shell-w","errorCode":null,"errorMessage":"error starting shell: %w","messagePattern":"error starting shell: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/agent/tools/bash.go","lineNumber":311,"sourceCode":"\t\t\t\t\tEndTime:          time.Now().UnixMilli(),\n\t\t\t\t\tDescription:      params.Description,\n\t\t\t\t\tWorkingDirectory: bgShell.WorkingDir,\n\t\t\t\t\tBackground:       true,\n\t\t\t\t\tShellID:          bgShell.ID,\n\t\t\t\t}\n\t\t\t\tresponse := fmt.Sprintf(\"Background shell started with ID: %s\\n\\nUse job_output tool to view output or job_kill to terminate.\", bgShell.ID)\n\t\t\t\treturn fantasy.WithResponseMetadata(fantasy.NewTextResponse(response), metadata), nil\n\t\t\t}\n\n\t\t\t// Start synchronous execution with auto-background support\n\t\t\tstartTime := time.Now()\n\n\t\t\t// Start with detached context so it can survive if moved to background\n\t\t\tbgManager := shell.GetBackgroundShellManager()\n\t\t\tbgManager.Cleanup()\n\t\t\tbgShell, err := bgManager.Start(context.Background(), execWorkingDir, blockFuncs(), params.Command, params.Description)\n\t\t\tif err != nil {\n\t\t\t\treturn fantasy.ToolResponse{}, fmt.Errorf(\"error starting shell: %w\", err)\n\t\t\t}\n\n\t\t\t// Wait for either completion, auto-background threshold, or context cancellation\n\t\t\tticker := time.NewTicker(100 * time.Millisecond)\n\t\t\tdefer ticker.Stop()\n\n\t\t\tautoBackgroundAfter := cmp.Or(params.AutoBackgroundAfter, DefaultAutoBackgroundAfter)\n\t\t\tautoBackgroundThreshold := time.Duration(autoBackgroundAfter) * time.Second\n\t\t\ttimeout := time.After(autoBackgroundThreshold)\n\n\t\t\tvar stdout, stderr string\n\t\t\tvar done bool\n\t\t\tvar execErr error\n\n\t\twaitLoop:\n\t\t\tfor {\n\t\t\t\tselect {\n\t\t\t\tcase <-ticker.C:","sourceCodeStart":293,"sourceCodeEnd":329,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/agent/tools/bash.go#L293-L329","documentation":"For foreground execution the tool also spawns the command through the background shell manager with a detached context (so it can be auto-backgrounded on timeout). If bgManager.Start fails to spawn the process, the tool returns 'error starting shell' wrapping the cause.","triggerScenarios":"Synchronous bash call where the working directory is invalid or inaccessible, the shell executable is unavailable, fork/exec is blocked by sandbox/limits, or the background manager's internal Start errors — the process never launches at all.","commonSituations":"params.WorkingDir points to a deleted directory; running inside restricted containers (no /bin/sh, seccomp denying fork); resource exhaustion (PID/fd limits) from prior leaked shells; permissions changed on the working dir.","solutions":["Confirm the working directory (params.WorkingDir or tool default) exists and is accessible","Check the wrapped cause for the exact OS error and address it (PATH, permissions, disk, ulimit)","Ensure the environment allows spawning processes (sandbox/container policy, fd/PID limits)","Retry after fixing environment; if the manager stays broken, restart the application"],"exampleFix":"// before\nBash(command=\"make\", working_dir=\"/deleted/build\")\n// after\nBash(command=\"make\", working_dir=\"/repo/build\")","handlingStrategy":"validation","validationCode":"wd := cmp.Or(params.WorkingDir, defaultDir)\nif info, err := os.Stat(wd); err != nil || !info.IsDir() {\n    return fmt.Errorf(\"invalid working dir %q\", wd)\n}\nif _, err := exec.LookPath(\"sh\"); err != nil {\n    return fmt.Errorf(\"no shell on PATH\")\n}","typeGuard":null,"tryCatchPattern":"bgShell, err := bgManager.Start(ctx, wd, blockers, cmd, desc)\nif err != nil {\n    var pe *fs.PathError\n    if errors.As(err, &pe) { /* fix path/permissions */ }\n    return fmt.Errorf(\"cannot start shell: %w\", err)\n}","preventionTips":["Pass only existing, accessible directories as WorkingDir","Verify container/sandbox permits process spawning","Watch for fd/PID exhaustion when many commands run concurrently; clean up finished shells"],"tags":["go","shell","process-spawn"],"backgroundTag":"process-start-failed","analyzedSha":"7944b8e52225d8805e31eacbf7ef24856b0dfb7a","analyzedAt":"2026-08-29T12:48:59.079Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}