{"record":{"id":"a3704c4ffc774d8c","repo":"crowdsecurity/crowdsec","slug":"blocklist-url-is-nil","errorCode":null,"errorMessage":"blocklist URL is nil","messagePattern":"blocklist URL is nil","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/apiclient/decisions_service.go","lineNumber":177,"sourceCode":"\t\t\tpartialDecisions[idx] = &models.Decision{\n\t\t\t\tScenario: &scenarioDeleted,\n\t\t\t\tScope:    decisionsGroup.Scope,\n\t\t\t\tType:     new(types.DecisionTypeBan),\n\t\t\t\tValue:    &decision,\n\t\t\t\tDuration: &durationDeleted,\n\t\t\t\tOrigin:   new(types.CAPIOrigin),\n\t\t\t}\n\t\t}\n\n\t\tv2Decisions.Deleted = append(v2Decisions.Deleted, partialDecisions...)\n\t}\n\n\treturn &v2Decisions, resp, nil\n}\n\nfunc (*DecisionsService) GetDecisionsFromBlocklist(ctx context.Context, blocklist *modelscapi.BlocklistLink, lastPullTimestamp string) ([]*models.Decision, bool, error) {\n\tif blocklist.URL == nil {\n\t\treturn nil, false, errors.New(\"blocklist URL is nil\")\n\t}\n\n\tlog.Debugf(\"Fetching blocklist %s\", *blocklist.URL)\n\n\tclient := http.Client{}\n\n\treq, err := http.NewRequestWithContext(ctx, http.MethodGet, *blocklist.URL, http.NoBody)\n\tif err != nil {\n\t\treturn nil, false, err\n\t}\n\n\tif lastPullTimestamp != \"\" {\n\t\treq.Header.Set(\"If-Modified-Since\", lastPullTimestamp)\n\t}\n\n\tlog.Debugf(\"[URL] %s %s\", req.Method, req.URL)\n\n\t// we don't use client_http Do method because we need the reader and is not provided.","sourceCodeStart":159,"sourceCodeEnd":195,"githubUrl":"https://github.com/crowdsecurity/crowdsec/blob/909b5157986a2b2c2163300fdaef5ed01289f7d2/pkg/apiclient/decisions_service.go#L159-L195","documentation":"GetDecisionsFromBlocklist fetches community-blocklist content from a BlocklistLink; the URL to fetch lives in blocklist.URL. If the URL pointer is nil the blocklist entry has no source address, and fetching is impossible, so the function returns this error early.","triggerScenarios":"Calling GetDecisionsFromBlocklist with a BlocklistLink parsed from CAPI metadata whose url field was absent/JSON null, or a hand-constructed BlocklistLink without setting URL.","commonSituations":"CAPI returns a blocklist entry with a null url (e.g. malformed or new link type not yet populated), older CAPI response formats, custom code iterating all blocklist links including ones without URLs.","solutions":["Skip blocklists with nil URL before calling: if bl.URL != nil { ... }","Update crowdsec so the CAPI client parses the blocklist metadata correctly and filters null-URL entries.","Log and continue on this error for individual blocklists instead of aborting the whole pull."],"exampleFix":"// before\nfor _, bl := range blocklists {\n    decisions, changed, err := svc.GetDecisionsFromBlocklist(ctx, bl, lastPull)\n// after\nfor _, bl := range blocklists {\n    if bl.URL == nil {\n        log.Warnf(\"blocklist %s has no URL, skipping\", bl.Name)\n        continue\n    }\n    decisions, changed, err := svc.GetDecisionsFromBlocklist(ctx, bl, lastPull)","handlingStrategy":"validation","validationCode":"if blocklist == nil || blocklist.URL == nil || *blocklist.URL == \"\" {\n    return nil // skip\n}","typeGuard":"func hasURL(b *modelscapi.BlocklistLink) bool {\n    return b != nil && b.URL != nil && *b.URL != \"\"\n}","tryCatchPattern":"decisions, changed, err := svc.GetDecisionsFromBlocklist(ctx, bl, ts)\nif err != nil {\n    log.Warnf(\"blocklist fetch failed: %v\", err)\n    continue\n}","preventionTips":["Filter blocklist links for non-nil URLs before iterating.","Keep crowdsec updated so CAPI blocklist metadata parsing stays current.","Treat per-blocklist errors as non-fatal and continue the pull."],"tags":["blocklist","capi","nil-pointer"],"backgroundTag":"missing-required-argument","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"}