golang/go · error

command cannot be run in background

Error message

command cannot be run in background

What it means

Returned by Engine.runCommand when the parsed line ends with a background marker '&' (cmd.background == true) but the resolved command's Usage declares it as non-async. Only commands that opt in via CmdUsage{Async: true} may be backgrounded; attempting to background a synchronous command (like cd, mkdir, setenv) is rejected.

Source

Thrown at src/cmd/internal/script/engine.go:555

		if err != nil {
			return false, fmt.Errorf("evaluating condition %q: %w", cond.tag, err)
		}
		if active != cond.want {
			return false, nil
		}
	}

	return true, nil
}

func (e *Engine) runCommand(s *State, cmd *command, impl Cmd) error {
	if impl == nil {
		return cmdError(cmd, errors.New("unknown command"))
	}

	async := impl.Usage().Async
	if cmd.background && !async {
		return cmdError(cmd, errors.New("command cannot be run in background"))
	}

	wait, runErr := impl.Run(s, cmd.args...)
	if wait == nil {
		if async && runErr == nil {
			return cmdError(cmd, errors.New("internal error: async command returned a nil WaitFunc"))
		}
		return checkStatus(cmd, runErr)
	}
	if runErr != nil {
		return cmdError(cmd, errors.New("internal error: command returned both an error and a WaitFunc"))
	}

	if cmd.background {
		s.background = append(s.background, backgroundCmd{
			command: cmd,
			wait:    wait,
		})

View on GitHub (pinned to b6b368adc5)

Solutions

  1. Remove the trailing '&' from the command — it does not support background execution.
  2. If you truly need concurrency, use a command declared Async (e.g. an exec-like command) instead.
  3. Check impl.Usage().Async to learn which commands may be backgrounded in your engine.

Example fix

// before
cd subdir &
# -> command cannot be run in background

// after
cd subdir
Defensive patterns

Strategy: validation

Validate before calling

// Check whether a command may be backgrounded before writing the '&'.
func canBackground(name string, cmds map[string]script.Cmd) bool {
    c, ok := cmds[name]
    if !ok { return false }
    return c.Usage().Async
}

Type guard

func isCantBackground(err error) bool {
    var ce *script.CommandError
    return errors.As(err, &ce) && ce.Err != nil && ce.Err.Error() == "command cannot be run in background"
}

Try / catch

// Remove trailing '&' from non-async commands.

Prevention

When it happens

Trigger: A script line appending '&' to a non-async command, e.g. `cd dir &`, `mkdir x &`, `setenv K v &`. The parser sets cmd.background; runCommand reads impl.Usage().Async and, finding it false, returns the error.

Common situations: Script-test author assumes all commands support backgrounding (only long-running ones like exec do). Trailing '&' left from converting a shell snippet into a script test.

Related errors


AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12). Data as JSON: /api/errors/146ce6d506d81e8d. Report an issue: GitHub.