{"record":{"id":"999c36eae12fd15b","repo":"crowdsecurity/crowdsec","slug":"unexpected-status-code-d","errorCode":null,"errorMessage":"unexpected status code: %d","messagePattern":"unexpected status code: (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/longpollclient/client.go","lineNumber":106,"sourceCode":"\t\treturn err\n\t}\n\n\tdefer resp.Body.Close()\n\n\trequestId := resp.Header.Get(\"X-Amzn-Trace-Id\")\n\tlogger = logger.WithField(\"request-id\", requestId)\n\tif resp.StatusCode != http.StatusOK {\n\t\tc.logger.Errorf(\"unexpected status code: %d\", resp.StatusCode)\n\t\tif resp.StatusCode == http.StatusPaymentRequired {\n\t\t\tbodyContent, err := io.ReadAll(resp.Body)\n\t\t\tif err != nil {\n\t\t\t\tlogger.Errorf(\"failed to read response body: %s\", err)\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tlogger.Error(string(bodyContent))\n\t\t\treturn errUnauthorized\n\t\t}\n\t\treturn fmt.Errorf(\"unexpected status code: %d\", resp.StatusCode)\n\t}\n\n\tdecoder := json.NewDecoder(resp.Body)\n\n\tfor {\n\t\tselect {\n\t\tcase <-c.t.Dying():\n\t\t\tlogger.Debugf(\"dying\")\n\t\t\tclose(c.c)\n\t\t\treturn nil\n\t\tcase <-ctx.Done():\n\t\t\tlogger.Debugf(\"context canceled\")\n\t\t\tclose(c.c)\n\t\t\treturn ctx.Err()\n\t\tdefault:\n\t\t\tvar pollResp pollResponse\n\t\t\terr = decoder.Decode(&pollResp)\n\t\t\tif err != nil {","sourceCodeStart":88,"sourceCodeEnd":124,"githubUrl":"https://github.com/crowdsecurity/crowdsec/blob/909b5157986a2b2c2163300fdaef5ed01289f7d2/pkg/longpollclient/client.go#L88-L124","documentation":"The longpollclient's poll() performs a streaming HTTP request to the LAPI (or other endpoint) and expects a 200 response before decoding the JSON stream. If the response carries any other status code, it returns 'unexpected status code: %d'. 401 responses are special-cased into errUnauthorized earlier in the function, so any other non-200 (403, 404, 500, 502…) surfaces through this error, aborting the poll loop.","triggerScenarios":"pollEvents → poll() receives resp.StatusCode != 200 and != 401: LAPI not reachable behind a reverse proxy returning 502/503, wrong URL/port in config (404), bouncer/user credentials valid enough to pass preflight but lacking rights (403), or LAPI crashed mid-request (500).","commonSituations":"Long-polling bouncers (appsec/notification setups) pointing at a wrong api_url; TLS proxy returning HTML error pages with 502; LAPI restarted during polling; version mismatch producing 404 on the poll route.","solutions":["Check the reported status code: 404 → wrong api_url/path, 403 → permissions, 5xx → LAPI or proxy problem","Verify api_url, port, and route in the client's configuration (cscli bouncers / LAPI address)","Check LAPI and any reverse-proxy logs around the failure time","Confirm the client's credentials exist and are valid (cscli bouncers list / validate), then retry"],"exampleFix":"// before (config)\napi_url: http://127.0.0.1:8081/api/v1/wrong-route\n// after\napi_url: http://127.0.0.1:8080/","handlingStrategy":"retry","validationCode":"# Before starting the poller, probe the endpoint:\ncurl -s -o /dev/null -w '%{http_code}' -H \"Authorization: Bearer $KEY\" \"$API_URL/watch/login/decision\"","typeGuard":"// Treat only 200 as success; handle known codes explicitly:\nswitch resp.StatusCode {\ncase http.StatusOK: /* proceed */\ncase http.StatusUnauthorized: /* re-auth */\ndefault: return fmt.Errorf(\"unexpected status code: %d\", resp.StatusCode)\n}","tryCatchPattern":"if err := poller.Start(ctx); err != nil {\n    if strings.Contains(err.Error(), \"unexpected status code\") {\n        // log the code, check LAPI health, backoff and retry\n        time.Sleep(retryBackoff)\n    }\n}","preventionTips":["Verify api_url and route reachability before deploying bouncers","Check reverse-proxy timeouts for long-poll endpoints","Keep client and LAPI versions compatible","Validate bouncer credentials with cscli bouncers list","Monitor LAPI health to catch 5xx before clients do"],"tags":["http","longpoll","lapi","network"],"backgroundTag":"unexpected-http-status","analyzedSha":"909b5157986a2b2c2163300fdaef5ed01289f7d2","analyzedAt":"2026-09-06T12:27:26.012Z","contentChangedAt":"2026-09-06T12:27:26.012Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}