{"record":{"id":"389fcd41dc1f2f05","repo":"semaphoreui/semaphore","slug":"webhook-returned-incorrect-status","errorCode":null,"errorMessage":"webhook returned incorrect status","messagePattern":"webhook returned incorrect status","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"services/tasks/RemoteJob.go","lineNumber":79,"sourceCode":"\treq, err = http.NewRequest(\"POST\", runner.Webhook, bytes.NewBuffer(jsonBytes))\n\tif err != nil {\n\t\treturn\n\t}\n\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\n\tvar resp *http.Response\n\tresp, err = client.Do(req)\n\tif err != nil {\n\t\treturn\n\t}\n\n\tif resp != nil {\n\t\tdefer resp.Body.Close() //nolint:errcheck\n\t}\n\n\tif resp.StatusCode != 200 && resp.StatusCode != 204 {\n\t\terr = fmt.Errorf(\"webhook returned incorrect status\")\n\t\treturn\n\t}\n\n\treturn\n}\n\nfunc shuffleRunners(rs []db.Runner) []db.Runner {\n\tif len(rs) < 2 {\n\t\treturn rs\n\t}\n\n\t// Work on a copy so that if randomness fails, we can safely return the original order.\n\tshuffled := make([]db.Runner, len(rs))\n\tcopy(shuffled, rs)\n\n\t// Fisher–Yates shuffle using crypto/rand: for each i, pick j in [0, i].\n\tfor i := len(shuffled) - 1; i > 0; i-- {\n\t\tmax := big.NewInt(int64(i + 1))","sourceCodeStart":61,"sourceCodeEnd":97,"githubUrl":"https://github.com/semaphoreui/semaphore/blob/1774ccb71a0a8b82eb74ea24c23ac9ab713de2fa/services/tasks/RemoteJob.go#L61-L97","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Check the runner's logs for the incoming webhook request and its response status","Verify the webhook URL configured for the runner is correct and reachable from the server","Ensure any proxy/load-balancer in front of the runner forwards the request unchanged and passes 2xx through","Confirm runner and server versions are compatible (same webhook API contract)","Retry the task if the runner was temporarily unavailable"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// pre-check endpoint health before webhook delivery\nresp, err := http.Head(runnerWebhookURL)\nif err != nil || resp.StatusCode >= 500 { /* runner unhealthy, defer */ }","typeGuard":"func webhookAccepted(code int) bool { return code == 200 || code == 204 }","tryCatchPattern":"if err := callRunnerWebhook(...); err != nil {\n    if strings.Contains(err.Error(), \"webhook returned incorrect status\") {\n        // retry with backoff or re-queue the task\n    }\n}","preventionTips":["Monitor runner webhook endpoints for 4xx/5xx responses","Keep runner and server versions aligned on the webhook contract","Bypass or correctly configure proxies in front of runners","Accept that only 200/204 are valid success codes when implementing runners"],"tags":["go","webhook","http"],"backgroundTag":"http-error-response","analyzedSha":"1774ccb71a0a8b82eb74ea24c23ac9ab713de2fa","analyzedAt":"2026-09-07T11:00:33.293Z","contentChangedAt":"2026-09-07T11:00:33.293Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}