GitHub Actions Label-Based Routing Sends Jobs to the Wrong Runner
By Kaveh Alemi·Latchkey
A job lands on the wrong runner pool because multiple fleets share a label. With overlapping labels, GitHub may dispatch to any matching runner, so a job meant for GPU or arm64 runs somewhere else.
What this error means
A job runs on an unexpected runner - wrong architecture, missing tools, or a pool you meant to reserve - because more than one runner set matched the requested labels.
.github/workflows/ci.yml
# both an x64 and an arm64 fleet are labeled [self-hosted, build]runs-on:[self-hosted, build] # may match either fleet
Common causes
Labels overlap across fleets
When two runner groups share the same labels, any of them can claim a matching job, so routing is not deterministic.
Too-generic label set
Requesting only broad labels (self-hosted, linux) without a distinguishing one lets unintended runners pick up the job.
How to fix it
Add a distinguishing label
Give each fleet a unique label and request it so only the intended pool matches.
Assign unique, specific labels per fleet and request the exact set.
Use runner groups to restrict which repos and jobs reach a fleet.
Avoid relying on broad shared labels for routing-sensitive jobs.
How to prevent it
Give every runner fleet a unique distinguishing label.
Request specific labels for routing-sensitive jobs.
Isolate sensitive fleets behind runner groups.
Frequently asked questions
What causes "Runner label routing"?
When two runner groups share the same labels, any of them can claim a matching job, so routing is not deterministic.
How do I fix Runner label routing?
Give each fleet a unique label and request it so only the intended pool matches.
Can Latchkey fix this automatically?
Yes. Latchkey runs your GitHub Actions on managed runners that detect this failure, apply the fix, and retry the job automatically - self-healing is on by default.