semaphoreui/semaphore · error

webhook returned incorrect status

Error message

webhook returned incorrect status

What it means

callRunnerWebhook delivers a task notification to a runner's webhook endpoint and accepts only HTTP 200 and 204 as success. Any other status code from the runner webhook yields this error, which callers (Run, finalizeRemoteTaskLocked) surface while finalizing the remote task.

Solutions

  1. Check the runner's logs for the incoming webhook request and its response status
  2. Verify the webhook URL configured for the runner is correct and reachable from the server
  3. Ensure any proxy/load-balancer in front of the runner forwards the request unchanged and passes 2xx through
  4. Confirm runner and server versions are compatible (same webhook API contract)
  5. Retry the task if the runner was temporarily unavailable
Defensive patterns

Strategy: retry

Validate before calling

// pre-check endpoint health before webhook delivery
resp, err := http.Head(runnerWebhookURL)
if err != nil || resp.StatusCode >= 500 { /* runner unhealthy, defer */ }

Type guard

func webhookAccepted(code int) bool { return code == 200 || code == 204 }

Try / catch

if err := callRunnerWebhook(...); err != nil {
    if strings.Contains(err.Error(), "webhook returned incorrect status") {
        // retry with backoff or re-queue the task
    }
}

Prevention

When it happens

Trigger: The runner's webhook URL responds with 4xx/5xx (auth failure at the runner, runner restarting, wrong webhook route, proxy interference) or any non-200/204 code such as 301/302 redirects that the HTTP client did not follow.

Common situations: Runner behind a misconfigured reverse proxy returning 502/503; webhook endpoint requires auth that the server does not provide; runner version changed its API and now returns a different success status; load balancer health checks intercepting the URL.

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


AI-assisted analysis of semaphoreui/semaphore@1774ccb71a (2026-09-07). Data as JSON: /api/errors/389fcd41dc1f2f05. Report an issue: GitHub.

Appendix: source

Thrown at services/tasks/RemoteJob.go:79

	req, err = http.NewRequest("POST", runner.Webhook, bytes.NewBuffer(jsonBytes))
	if err != nil {
		return
	}

	req.Header.Set("Content-Type", "application/json")

	var resp *http.Response
	resp, err = client.Do(req)
	if err != nil {
		return
	}

	if resp != nil {
		defer resp.Body.Close() //nolint:errcheck
	}

	if resp.StatusCode != 200 && resp.StatusCode != 204 {
		err = fmt.Errorf("webhook returned incorrect status")
		return
	}

	return
}

func shuffleRunners(rs []db.Runner) []db.Runner {
	if len(rs) < 2 {
		return rs
	}

	// Work on a copy so that if randomness fails, we can safely return the original order.
	shuffled := make([]db.Runner, len(rs))
	copy(shuffled, rs)

	// Fisher–Yates shuffle using crypto/rand: for each i, pick j in [0, i].
	for i := len(shuffled) - 1; i > 0; i-- {
		max := big.NewInt(int64(i + 1))

View on GitHub (pinned to 1774ccb71a)