The manual is clear on this: Since the coprocess is created as an asynchronous command, the coproc command always returns success
I'd argue that it should return an error code for this particular case though: $ bash -c 'coproc % { :; }; echo $?' bash: `%': not a valid identifier 0 The `execute_coproc' function _seems_ to try to return an error code for this case (by the way, the value of `invert' is undefined at this point): 2301 /* XXX - expand coproc name without splitting -- bash-5.0 */ 2302 /* could make this dependent on a shopt option */ 2303 name = expand_string_unsplit_to_string (command->value.Coproc->name, 0); 2304 /* Optional check -- bash-5.0. */ 2305 if (legal_identifier (name) == 0) 2306 { 2307 internal_error (_("`%s': not a valid identifier"), name); 2308 return (invert ? EXECUTION_SUCCESS : EXECUTION_FAILURE); 2309 } 2310 else 2311 { 2312 free (command->value.Coproc->name); 2313 command->value.Coproc->name = name; 2314 } The following patch fixes this (coproc returns $? = 1 when `NAME' is not a legal identifier), although I'm not convinced about the quality of this patch. dualbus@debian:~/src/gnu/bash$ git diff diff --git a/execute_cmd.c b/execute_cmd.c index 0183a105..ab022bdb 100644 --- a/execute_cmd.c +++ b/execute_cmd.c @@ -597,8 +597,9 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out, return (execute_in_subshell (command, asynchronous, pipe_in, pipe_out, fds_to_close)); #if defined (COPROCESS_SUPPORT) - if (command->type == cm_coproc) - return (execute_coproc (command, pipe_in, pipe_out, fds_to_close)); + if (command->type == cm_coproc) { + return (last_command_exit_value = execute_coproc (command, pipe_in, pipe_out, fds_to_close)); + } #endif user_subshell = command->type == cm_subshell || ((command->flags & CMD_WANT_SUBSHELL) != 0); @@ -2298,6 +2299,8 @@ execute_coproc (command, pipe_in, pipe_out, fds_to_close) coproc_init (&sh_coproc); #endif + invert = (command->flags & CMD_INVERT_RETURN) != 0; + /* XXX - expand coproc name without splitting -- bash-5.0 */ /* could make this dependent on a shopt option */ name = expand_string_unsplit_to_string (command->value.Coproc->name, 0); @@ -2313,7 +2316,6 @@ execute_coproc (command, pipe_in, pipe_out, fds_to_close) command->value.Coproc->name = name; } - invert = (command->flags & CMD_INVERT_RETURN) != 0; command_string_index = 0; tcmd = make_command_string (command); This came up as a warning from cppcheck: [../bash/execute_cmd.c:2308]: (error) Uninitialized variable: invert -- Eduardo Bustamante https://dualbus.me/