{"version":"2.1.271","anchor":"self-hosted-runner-now-reports-structured-drain-hold-outcome","canonical_anchor":"self-hosted-runner-now-reports-structured-drain-hold-outcome","heading":"Self-hosted runner reports drain details when it shuts down","tier":"internal","area":"Self-Hosted Runner","scope":null,"heads_up":null,"url":"https:\/\/changelogs.core-directive.com\/v\/2.1.271\/e\/self-hosted-runner-now-reports-structured-drain-hold-outcome","release_url":"https:\/\/changelogs.core-directive.com\/v\/2.1.271","markdown":"### Self-hosted runner reports drain details when it shuts down\n\nSelf-hosted runners now send a reason and detailed drain-hold outcome when they deregister on shutdown\n\n**What**\n\nWhen a self-hosted session runner shuts down, its deregistration call (`deregisterRunner`) now carries more detail about how the shutdown happened.\n\n- A `reason` field records why the runner is deregistering: `host_drain`, `shutdown_signal`, `retire_at`, or `idle_release`.\n\n- A `drain_hold` object records what happened while the runner waited for in-progress work to finish: an `outcome` (`RUNNER_DRAIN_HOLD_OUTCOME_NOT_NEEDED`, `_CAP_EXPIRED`, or `_FINISHED`), how long the hold was capped at (`cap_ms`) and actually lasted (`held_ms`), and a capped list of session IDs that were still busy when the runner was released (`busy_session_ids_at_release`).\n\nBoth fields are sent in the `deregisterRunner` POST body and logged.\n\n**Why**\n\nThis gives operators visibility into why and how a self-hosted runner drained on shutdown, including whether it had to wait for busy sessions and what was still running when it let go.\n\n- Area: Self-Hosted Runner\n- Tier: Under the hood\n- Useful: 2\/5\n- Signal: 2\/5"}