semaphoreui/semaphore · error
runner is not registered
Error message
runner is not registered
What it means
Unregister was called but the runner has no registration token in config (util.Config.Runner.Token is empty), meaning it was never registered (or the token was removed). Unregistering without a token is meaningless, so it fails immediately without making any HTTP request.
Solutions
- Only call Unregister after a successful Register; guard the shutdown path on the runner's registered state.
- If the runner should be registered, run Register first to obtain and persist a token.
- If unregistering manually, ensure the config file contains the runner's token.
Example fix
// before
pool.Unregister()
// after
if util.Config.Runner.Token != "" {
if err := pool.Unregister(); err != nil { log.Error(err) }
} Defensive patterns
Strategy: validation
Validate before calling
if util.Config.Runner.Token == "" {
log.Info("runner not registered; skipping unregister")
return nil
}
return pool.Unregister() Type guard
func isRegistered() bool { return util.Config.Runner.Token != "" } Try / catch
if err := pool.Unregister(); err != nil {
if strings.Contains(err.Error(), "not registered") {
return nil // nothing to do
}
return err
} Prevention
- Track registration state in the runner lifecycle and gate Unregister on it.
- Call Unregister only after a successful Register.
- Persist the token so restarts know the runner is registered.
When it happens
Trigger: Calling JobPool.Unregister() (via unregisterRunner) when the runner config has no Runner.Token — e.g. unregistering a runner that never completed Register, or after the token was blanked in config.
Common situations: Shutdown/cleanup path runs even when startup registration failed; operator runs the unregister command against a fresh runner that was never registered; config file regenerated without the token.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- encountered error while unregistering runner; server…
- OIDC sign-in failed: invalid state. Try signing in again.
- Empty token
- unknown runner executor type
- executor provider is not initialised (check runner executor…
AI-assisted analysis of semaphoreui/semaphore@1774ccb71a (2026-09-07).
Data as JSON: /api/errors/2e6bec03c7e9b6b0.
Report an issue: GitHub.
Appendix: source
Thrown at services/runners/job_pool.go:221
return false
}
func (p *JobPool) Register(configFilePath *string) (err error) {
ok := p.tryRegisterRunner(configFilePath)
if !ok {
err = fmt.Errorf("runner registration failed")
return
}
return
}
func (p *JobPool) Unregister() (err error) {
if util.Config.Runner.Token == "" {
return fmt.Errorf("runner is not registered")
}
url := util.Config.WebHost + "/api/internal/runners"
req, err := http.NewRequest("DELETE", url, nil)
if err != nil {
return
}
log.WithFields(log.Fields{
"context": "unregistration",
"url": url,
}).Debug("Sending unregistration request to the server")
resp, err := p.client.Do(req)
if err != nil {
return
}View on GitHub (pinned to 1774ccb71a)