Skip to content

Windows: cancel on one background step interrupts every other running step #4766

Description

@raymond-UI

Describe the bug
On Windows, cancel: on one background step also interrupts every other step that is running at that moment. The step you didn't cancel gets the same Ctrl+C and fails. On Linux only the cancelled step is affected.

To Reproduce
Minimal public repro: https://github.com/raymond-UI/runner-background-cancel-repro (workflow, run)

- name: Target
  id: target
  background: true
  run: node -e "process.on('SIGINT',()=>{console.log('target got SIGINT');process.exit(0)}); setInterval(()=>{},1000)"
- name: Bystander
  id: bystander
  background: true
  run: node -e "process.on('SIGINT',()=>{console.log('bystander got SIGINT');process.exit(3)}); setTimeout(()=>console.log('bystander finished on its own'),20000)"
- shell: bash
  run: sleep 3
- cancel: target
- wait: bystander

Expected behavior
Only Target is cancelled. Bystander keeps running and finishes on its own after 20 s, as it does on ubuntu-latest.

Runner Version and Platform

2.337.0, GitHub-hosted windows-latest. Also reproduced on a self-hosted Windows 11 runner on 2.337.0.

What's not working?

runs bystander survives
windows-latest 5 0
ubuntu-latest 5 5

Likely cause, from the source:

  • ScriptHandler passes inheritConsoleHandler: !Retain_Default_Encoding, which is true by default. So ProcessInvoker starts every script step with CreateNoWindow = false, and all steps share the worker's console.
  • ProcessInvoker.SendCtrlSignal does AttachConsole(_proc.Id), then GenerateConsoleCtrlEvent(signal, 0). Process group 0 means every process attached to that console, so every running step gets the event.
  • The Ctrl+Break fallback after 7.5 s has the same reach. On our self-hosted runner, where the Ctrl+C was ignored, the bystander's pwsh dropped into its debugger and the step failed.

This was harmless when cancel only ran for the whole job. With background steps it becomes reachable. Starting each step in its own process group (CREATE_NEW_PROCESS_GROUP) and signalling that group, rather than group 0, would limit the event to the cancelled step's tree.

Job Log Output

windows-latest, attempt 1:

05:40:55.4893636Z Cancelling background step(s): Target
05:40:55.5060473Z bystander got SIGINT
05:40:55.5060482Z target got SIGINT
05:40:55.5663204Z Finished cancelling background step(s).
05:40:55.5663643Z   Target: Canceled
05:40:55.5888725Z ##[error]Process completed with exit code 1.
05:40:55.7343108Z Waiting for background step(s) to complete: Bystander
05:40:55.7354311Z Finished waiting for background step(s).
05:40:55.7354649Z   Bystander: Failed

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions