@@ -38,6 +38,19 @@ export function resolvePowerShellPath(): string {
3838 return "powershell.exe" ;
3939}
4040
41+ // Non-interactive placeholder shells that reject "-c"-style invocations.
42+ // macOS LaunchDaemon service users commonly use /usr/bin/false so login sessions
43+ // cannot be opened; honoring SHELL in that case causes every exec to exit 1.
44+ // See https://github.com/openclaw/openclaw/issues/69077.
45+ const NON_INTERACTIVE_SHELLS = new Set ( [ "false" , "nologin" ] ) ;
46+
47+ function isNonInteractiveShell ( shellPath : string ) : boolean {
48+ if ( ! shellPath ) {
49+ return false ;
50+ }
51+ return NON_INTERACTIVE_SHELLS . has ( path . basename ( shellPath ) ) ;
52+ }
53+
4154export function getShellConfig ( ) : { shell : string ; args : string [ ] } {
4255 if ( process . platform === "win32" ) {
4356 // Use PowerShell instead of cmd.exe on Windows.
@@ -51,7 +64,8 @@ export function getShellConfig(): { shell: string; args: string[] } {
5164 } ;
5265 }
5366
54- const envShell = process . env . SHELL ?. trim ( ) ;
67+ const rawEnvShell = process . env . SHELL ?. trim ( ) ;
68+ const envShell = rawEnvShell && ! isNonInteractiveShell ( rawEnvShell ) ? rawEnvShell : undefined ;
5569 const shellName = envShell ? path . basename ( envShell ) : "" ;
5670 // Fish rejects common bashisms used by tools, so prefer bash when detected.
5771 if ( shellName === "fish" ) {
@@ -64,8 +78,13 @@ export function getShellConfig(): { shell: string; args: string[] } {
6478 return { shell : sh , args : [ "-c" ] } ;
6579 }
6680 }
67- const shell = envShell && envShell . length > 0 ? envShell : "sh" ;
68- return { shell, args : [ "-c" ] } ;
81+ if ( envShell ) {
82+ return { shell : envShell , args : [ "-c" ] } ;
83+ }
84+ // Placeholder SHELL (or unset): prefer a resolved sh/bash on PATH so we do not
85+ // re-invoke the placeholder and get a spurious exitCode=1.
86+ const sh = resolveShellFromPath ( "sh" ) ?? resolveShellFromPath ( "bash" ) ;
87+ return { shell : sh ?? "sh" , args : [ "-c" ] } ;
6988}
7089
7190export function resolveShellFromPath ( name : string ) : string | undefined {
@@ -114,7 +133,7 @@ export function detectRuntimeShell(): string | undefined {
114133 }
115134
116135 const envShell = process . env . SHELL ?. trim ( ) ;
117- if ( envShell ) {
136+ if ( envShell && ! isNonInteractiveShell ( envShell ) ) {
118137 const name = normalizeShellName ( envShell ) ;
119138 if ( name ) {
120139 return name ;
0 commit comments