{"record":{"id":"577af13d1b5b8c6d","repo":"crowdsecurity/crowdsec","slug":"unexpected-error-w","errorCode":null,"errorMessage":"unexpected error: %w","messagePattern":"unexpected error: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/cticlient/ctiexpr/expr.go","lineNumber":132,"sourceCode":"\tctiClient.Logger.Infof(\"cti call for %s\", ip)\n\n\tbefore := time.Now()\n\n\tctiResp, err := ctiClient.GetIPInfo(ip)\n\tctiClient.Logger.Debugf(\"request for %s took %v\", ip, time.Since(before))\n\tif err != nil {\n\t\tswitch {\n\t\tcase errors.Is(err, cticlient.ErrUnauthorized):\n\t\t\tCTIApiEnabled = false\n\t\t\tctiClient.Logger.Errorf(\"Invalid API key provided, disabling CTI API\")\n\t\t\treturn &cticlient.SmokeItem{}, cticlient.ErrUnauthorized\n\t\tcase errors.Is(err, cticlient.ErrLimit):\n\t\t\tCTIBackOffUntil = time.Now().Add(CTIBackOffDuration)\n\t\t\tctiClient.Logger.Errorf(\"CTI API is throttled, will try again in %s\", CTIBackOffDuration)\n\t\t\treturn &cticlient.SmokeItem{}, cticlient.ErrLimit\n\t\tdefault:\n\t\t\tctiClient.Logger.Warnf(\"CTI API error : %s\", err)\n\t\t\treturn &cticlient.SmokeItem{}, fmt.Errorf(\"unexpected error: %w\", err)\n\t\t}\n\t}\n\n\tif err := CTICache.SetWithExpire(ip, ctiResp, CacheExpiration); err != nil {\n\t\tctiClient.Logger.Warningf(\"IpCTI : error while caching CTI : %s\", err)\n\t\treturn &cticlient.SmokeItem{}, cticlient.ErrUnknown\n\t}\n\n\tctiClient.Logger.Tracef(\"CTI response : %v\", *ctiResp)\n\n\treturn ctiResp, nil\n}\n","sourceCodeStart":114,"sourceCodeEnd":145,"githubUrl":"https://github.com/crowdsecurity/crowdsec/blob/909b5157986a2b2c2163300fdaef5ed01289f7d2/pkg/cticlient/ctiexpr/expr.go#L114-L145","documentation":"CrowdsecCTI handles known CTI errors specially (ErrDisabled, ErrNotFound, ErrLimit/backoff). Any other error returned by the underlying CTI client (network failure, authentication problem, unexpected server response, bad JSON) falls into the default case and is wrapped as 'unexpected error: ...' alongside a Warnf log.","triggerScenarios":"Calling the cti expr function when the underlying cticlient request fails with a non-categorized error: DNS/connectivity failure to the CTI endpoint, invalid API key (401) surfaced as a generic error, malformed API response, or TLS error.","commonSituations":"No outbound internet access from the CrowdSec host; wrong CTI API key in config; CTI API outage or changed response format; corporate proxy breaking HTTPS to the API.","solutions":["Read the wrapped inner error (and the 'CTI API error' warning log) for the concrete cause.","Check network connectivity from the CrowdSec host to the CTI endpoint (curl the API).","Verify the CTI API key is valid; re-register or update the key if 401/403.","Retry after transient network/server issues; consider relying on cache to reduce calls."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// check connectivity + key before bulk lookups\nresp, err := http.Get(ctiEndpoint)\nif err != nil || resp.StatusCode != 200 {\n    return fmt.Errorf(\"CTI endpoint unreachable or auth invalid\")\n}","typeGuard":null,"tryCatchPattern":"item, err := cti(ip)\nswitch {\ncase errors.Is(err, cticlient.ErrNotFound):\n    // unknown IP, proceed\ncase errors.Is(err, cticlient.ErrLimit):\n    // throttled, back off\ncase err != nil:\n    log.Warnf(\"CTI lookup failed unexpectedly: %v\", err)\n}","preventionTips":["Ensure outbound HTTPS to the CTI API works from the host","Validate the API key with a manual curl before deployment","Handle ErrDisabled/ErrLimit/ErrNotFound explicitly instead of falling to default","Cache results to reduce API dependency"],"tags":["cti","api","network"],"backgroundTag":"upstream-api-error","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"}