{"record":{"id":"0f192dc4da142a9c","repo":"crowdsecurity/crowdsec","slug":"invalid-type-for-ip-t","errorCode":null,"errorMessage":"invalid type for ip : %T","messagePattern":"invalid type for ip : %T","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/cticlient/ctiexpr/expr.go","lineNumber":93,"sourceCode":")\n\nfunc CrowdsecCTIInitCache(size int, ttl time.Duration) {\n\tCTICache = gcache.New(size).LRU().Build()\n\tCacheExpiration = ttl\n}\n\n// func CrowdsecCTI(ip string) (*cticlient.SmokeItem, error) {\nfunc CrowdsecCTI(params ...any) (any, error) {\n\tvar ip string\n\n\tif !CTIApiEnabled {\n\t\treturn &cticlient.SmokeItem{}, cticlient.ErrDisabled\n\t}\n\n\tvar ok bool\n\n\tif ip, ok = params[0].(string); !ok {\n\t\treturn &cticlient.SmokeItem{}, fmt.Errorf(\"invalid type for ip : %T\", params[0])\n\t}\n\n\tif val, err := CTICache.Get(ip); err == nil && val != nil {\n\t\tctiClient.Logger.Debugf(\"cti cache fetch for %s\", ip)\n\n\t\tret, ok := val.(*cticlient.SmokeItem)\n\t\tif ok {\n\t\t\treturn ret, nil\n\t\t}\n\n\t\tctiClient.Logger.Warningf(\"CrowdsecCTI: invalid type in cache, removing\")\n\n\t\tCTICache.Remove(ip)\n\t}\n\n\tif !CTIBackOffUntil.IsZero() && time.Now().Before(CTIBackOffUntil) {\n\t\t// ctiClient.Logger.Warningf(\"Crowdsec CTI client is in backoff mode, ending in %s\", time.Until(CTIBackOffUntil))\n\t\treturn &cticlient.SmokeItem{}, cticlient.ErrLimit","sourceCodeStart":75,"sourceCodeEnd":111,"githubUrl":"https://github.com/crowdsecurity/crowdsec/blob/909b5157986a2b2c2163300fdaef5ed01289f7d2/pkg/cticlient/ctiexpr/expr.go#L75-L111","documentation":"The `cti` expr helper function (CrowdsecCTI) expects its first parameter to be a string containing an IP address. If the caller passes a non-string value (e.g. net.IP, a number, or nil), the function returns this 'invalid type for ip : %T' error instead of performing the CTI lookup.","triggerScenarios":"Using cti(...) in an expression (whitelist, filter, enrichment) where the argument is not a plain string: passing Alert.Source.IP typed as net.IP, a parsed value, or nil rather than a string.","commonSituations":"Writing expr like `cti(Alert.Source.IP)` where the field resolves to a non-string type; passing a variable that is nil; constructing expressions programmatically with wrong parameter types.","solutions":["Convert the argument to a string: use Alert.Source.GetIP() or a string-typed field.","Guard against nil: `Alert.Source.IP != nil && cti(...)`.  Prefer string casts in expr.","Check the wrapped inner error shows the actual %T to identify what type you passed."],"exampleFix":"// before (expr)\ncti(Alert.Source.IP)\n// after\ncti(Alert.Source.GetIP())","handlingStrategy":"type-guard","validationCode":"ipStr, ok := rawIp.(string)\nif !ok {\n    return fmt.Errorf(\"cti() requires a string IP, got %T\", rawIp)\n}","typeGuard":"func isString(v interface{}) bool { _, ok := v.(string); return ok }","tryCatchPattern":"res, err := cti(Alert.Source.GetIP())\nif err != nil {\n    log.Warnf(\"cti lookup skipped: %v\", err)\n}","preventionTips":["Always pass string-typed IP fields (e.g. Alert.Source.GetIP()) to cti()","Guard against nil sources before calling","Check the %T in the error message when debugging types"],"tags":["expr","type-mismatch","cti"],"backgroundTag":"type-mismatch","analyzedSha":"909b5157986a2b2c2163300fdaef5ed01289f7d2","analyzedAt":"2026-09-06T12:27:26.012Z","contentChangedAt":"2026-09-06T12:27:26.012Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}