{"record":{"id":"11285b234858988a","repo":"fish2018/pansou","slug":"cloudscraper-not-initialized","errorCode":null,"errorMessage":"cloudscraper not initialized","messagePattern":"cloudscraper not initialized","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/discourse/discourse.go","lineNumber":166,"sourceCode":"func (p *DiscourseAsyncPlugin) Search(keyword string, ext map[string]interface{}) ([]model.SearchResult, error) {\n\tresult, err := p.SearchWithResult(keyword, ext)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn result.Results, nil\n}\n\n// SearchWithResult 执行搜索并返回包含IsFinal标记的结果\nfunc (p *DiscourseAsyncPlugin) SearchWithResult(keyword string, ext map[string]interface{}) (model.PluginSearchResult, error) {\n\t// 使用BaseAsyncPlugin的异步搜索能力\n\treturn p.AsyncSearchWithResult(keyword, p.searchImpl, p.MainCacheKey, ext)\n}\n\n// searchImpl 实现具体的搜索逻辑\nfunc (p *DiscourseAsyncPlugin) searchImpl(client *http.Client, keyword string, ext map[string]interface{}) ([]model.SearchResult, error) {\n\t// 检查 cloudscraper 是否初始化成功\n\tif p.scraper == nil {\n\t\treturn nil, fmt.Errorf(\"cloudscraper not initialized\")\n\t}\n\n\t// 提取 max_pages 参数（最多获取多少页）\n\tmaxPages := defaultMaxPages\n\tif maxPagesVal, ok := ext[\"max_pages\"]; ok {\n\t\tif maxPagesInt, ok := maxPagesVal.(int); ok {\n\t\t\tmaxPages = maxPagesInt\n\t\t} else if maxPagesFloat, ok := maxPagesVal.(float64); ok {\n\t\t\tmaxPages = int(maxPagesFloat)\n\t\t}\n\t}\n\t\n\t// 限制最大页数\n\tif maxPages > maxAllowedPages {\n\t\tmaxPages = maxAllowedPages\n\t}\n\tif maxPages < 1 {\n\t\tmaxPages = 1","sourceCodeStart":148,"sourceCodeEnd":184,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/plugin/discourse/discourse.go#L148-L184","documentation":"The Discourse async plugin's searchImpl returns this sentinel error when p.scraper is nil, i.e. the cloudscraper client required to make Cloudflare-resilient requests was never initialized. Unlike other plugins this message is a plain English string without the plugin name prefix.","triggerScenarios":"searchImpl is called (via the async search path) before or without a successful scraper initialization: Init/constructor failure, plugin registered but never configured, or instantiation in tests bypassing setup.","commonSituations":"cloudscraper init error swallowed at startup; plugin config missing required options for scraper creation; unit tests constructing the struct literal without running setup.","solutions":["Check startup logs for the cloudscraper initialization failure and fix it.","Ensure the plugin's initialization that assigns p.scraper always runs before searching.","Fail fast at registration time if the scraper cannot be created.","If a plain http.Client suffices for non-Cloudflare instances, add a fallback path instead of erroring."],"exampleFix":"// before\np := &DiscourseAsyncPlugin{}\nresults, err := p.Search(keyword) // error: cloudscraper not initialized\n// after\np := NewDiscourseAsyncPlugin()\nif err := p.Init(cfg); err != nil { log.Fatalf(\"init: %v\", err) }\nresults, err := p.Search(keyword)","handlingStrategy":"validation","validationCode":"if p.scraper == nil {\n    return errors.New(\"discourse plugin not initialized: cloudscraper is nil\")\n}\n// safe to search","typeGuard":null,"tryCatchPattern":"if err != nil {\n    if err.Error() == \"cloudscraper not initialized\" {\n        return fmt.Errorf(\"run plugin Init before searching: %w\", err)\n    }\n    return err\n}","preventionTips":["Initialize the plugin via its constructor/Init and check errors at startup.","Add fail-fast assertions that scraper is non-nil at registration.","Avoid raw struct-literal construction in production code and tests."],"tags":["cloudflare","initialization","nil-check","discourse","go"],"backgroundTag":"module-init-failed","analyzedSha":"beaa56133755a548ebc51b090b3816e2ae044aa6","analyzedAt":"2026-09-07T00:31:18.025Z","contentChangedAt":"2026-09-07T00:31:18.025Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}