semaphoreui/semaphore · error
503 Service Unavailable
Error message
503 Service Unavailable
What it means
Returned by the Metrics ServeHTTP handler in pkg/metrics/metrics.go when the receiver is nil or its embedded prometheus handler was never initialised (e.g. the Metrics object was constructed without registering a registry). It is a guard that degrades gracefully: instead of panicking on scrape, it answers HTTP 503 so the scraper knows metrics are temporarily unavailable rather than silently scraping an empty page.
Solutions
- Ensure metrics.New()/registration runs before the handler is mounted on the HTTP mux
- Check that the Metrics value is not a nil pointer (a common cause is returning a nil *Metrics from a failed constructor and then using it as a handler)
- Wire a live *prometheus.Registry into the handler during service startup so ServeHTTP never sees a nil inner handler
- Scrapers should treat 503 from this endpoint as 'metrics not ready' and retry after startup completes
Defensive patterns
Strategy: fallback
When it happens
Trigger: Thrown at pkg/metrics/metrics.go:72 when the library encounters an invalid state.
Common situations: See trigger scenarios.
Understand the failure class
- HTTP status errors: handling 4xx and 5xx responses — how to handle 4xx and 5xx responses properly.
AI-assisted analysis of semaphoreui/semaphore@1774ccb71a (2026-09-07).
Data as JSON: /api/errors/8621acfa74a5bd25.
Report an issue: GitHub.
Appendix: source
Thrown at pkg/metrics/metrics.go:72
return
}
if oldStatus == task_logger.TaskRunningStatus {
m.tasksRunning.Dec()
}
if newStatus == task_logger.TaskRunningStatus {
m.tasksRunning.Inc()
}
if newStatus.IsFinished() && !oldStatus.IsFinished() {
m.tasksTotal.WithLabelValues(string(newStatus)).Inc()
}
}
func (m *Metrics) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if m == nil || m.handler == nil {
http.Error(w, http.StatusText(http.StatusServiceUnavailable), http.StatusServiceUnavailable)
return
}
m.handler.ServeHTTP(w, r)
}
View on GitHub (pinned to 1774ccb71a)