semaphoreui/semaphore · error
encountered error while unregistering runner; server…
Error message
encountered error while unregistering runner; server returned code %d
What it means
The DELETE request to the server's internal runners endpoint returned an HTTP status >= 400 (and not 404, which is tolerated since the runner is effectively already gone). Unregister treats any other client/server error status as a failed unregistration and surfaces the status code.
Solutions
- Check the reported status code: 401/403 means the stored token is invalid — re-register or remove the runner manually in the server UI.
- Verify the server is healthy and reachable (status 5xx usually indicates server/proxy issues).
- Retry the unregister after the server recovers; 404 is already treated as success.
- If the runner was deleted server-side already, ignore the error or clear the local token.
Defensive patterns
Strategy: retry
Validate before calling
// no local pre-check possible; verify server health before unregister
if err := pingServer(util.Config.WebHost); err != nil {
return fmt.Errorf("server unreachable, defer unregister: %w", err)
} Try / catch
if err := pool.Unregister(); err != nil {
var statusErr interface{ StatusCode() int }
if strings.Contains(err.Error(), "server returned code 5") {
time.Sleep(10 * time.Second)
return pool.Unregister() // retry once after server recovers
}
return err
} Prevention
- Handle 404 as success (already unregistered).
- Retry unregister on 5xx; don't retry 4xx auth failures.
- Monitor runner tokens for revocation to avoid 401/403.
When it happens
Trigger: Unregister() sends DELETE to WebHost + /api/internal/runners and the response status is e.g. 401/403 (bad token), 500 (server-side failure), or 502/503 (proxy downtime).
Common situations: Stale or invalid runner token rejected by the server; server behind a reverse proxy returning 502 during maintenance; server bug while deleting the runner record.
Understand the failure class
Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.
Related errors
- runner is not registered
- Unauthorized
- internal error
- Account linking must be initiated with a POST request.
- You must be signed in to link an external account.
AI-assisted analysis of semaphoreui/semaphore@1774ccb71a (2026-09-07).
Data as JSON: /api/errors/353324040585dc28.
Report an issue: GitHub.
Appendix: source
Thrown at services/runners/job_pool.go:243
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
}
defer resp.Body.Close() //nolint:errcheck
if resp.StatusCode >= 400 && resp.StatusCode != 404 {
err = fmt.Errorf("encountered error while unregistering runner; server returned code %d", resp.StatusCode)
return
}
log.WithFields(log.Fields{
"context": "unregistration",
"status_code": resp.StatusCode,
}).Debug("Runner unregistered on the server")
if util.Config.Runner.TokenFile != "" {
err = os.Remove(util.Config.Runner.TokenFile)
}
return
}
// runnerProgressInterval is how often the runner reports progress. It is not
// configurable: the report is also the heartbeat the server uses to decide a
// runner is still alive.View on GitHub (pinned to 1774ccb71a)