{"record":{"id":"6e3e47a39a5f575c","repo":"sipeed/picoclaw","slug":"no-api-key-provided","errorCode":null,"errorMessage":"no API key provided","messagePattern":"no API key provided","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/tools/integration/web.go","lineNumber":285,"sourceCode":"\t\treturn nil\n\t}\n\treturn lens\n}\n\ntype BraveSearchProvider struct {\n\tkeyPool *APIKeyPool\n\tproxy   string\n\tclient  *http.Client\n}\n\nfunc (p *BraveSearchProvider) Search(\n\tctx context.Context,\n\tquery string,\n\tcount int,\n\trangeCode string,\n) (string, error) {\n\tif p.keyPool == nil || len(p.keyPool.keys) == 0 {\n\t\treturn \"\", errors.New(\"no API key provided\")\n\t}\n\n\tsearchURL := fmt.Sprintf(\"https://api.search.brave.com/res/v1/web/search?q=%s&count=%d\",\n\t\turl.QueryEscape(query), count)\n\tif freshness := mapBraveFreshness(rangeCode); freshness != \"\" {\n\t\tsearchURL += \"&freshness=\" + url.QueryEscape(freshness)\n\t}\n\n\tvar lastErr error\n\titer := p.keyPool.NewIterator()\n\n\tfor {\n\t\tapiKey, ok := iter.Next()\n\t\tif !ok {\n\t\t\tbreak\n\t\t}\n\n\t\treq, err := http.NewRequestWithContext(ctx, \"GET\", searchURL, nil)","sourceCodeStart":267,"sourceCodeEnd":303,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/pkg/tools/integration/web.go#L267-L303","documentation":"BraveSearchProvider.Search refuses to run when its APIKeyPool is nil or has zero keys. The pool is built by NewAPIKeyPool(keys) from configured Brave API keys; in the standard WebTool wiring, providers without credentials are not registered at all, so hitting this usually means direct provider construction or a config race.","triggerScenarios":"Calling Search on a BraveSearchProvider constructed with an empty key list (NewAPIKeyPool(nil) / NewAPIKeyPool([]string{})), i.e. brave is selected but no API key made it into the provider.","commonSituations":"Brave set as the web-search provider in picoclaw config without its API key(s); key env var not exported in the service unit; multiple keys configured as a single comma string instead of a list so parsing yields nothing.","solutions":["Configure the Brave API key(s) for the web search provider in picoclaw config (or the env var your deployment reads them from) and restart","Verify the key list actually reaches the provider: len(pool.Keys) > 0 before serving","If no Brave key is available, switch the provider to one with credentials or to SearXNG (needs only a URL)"],"exampleFix":"// before\np := &integration.BraveSearchProvider{} // no keyPool\n\n// after\npool := integration.NewAPIKeyPool([]string{os.Getenv(\"BRAVE_API_KEY\")})\np := &integration.BraveSearchProvider{KeyPool: pool}","handlingStrategy":"validation","validationCode":"keys := []string{os.Getenv(\"BRAVE_API_KEY\")}\nkeys = slices.DeleteFunc(keys, func(k string) bool { return strings.TrimSpace(k) == \"\" })\nif len(keys) == 0 {\n    return errors.New(\"brave selected but no API key configured\")\n}\np := &integration.BraveSearchProvider{KeyPool: integration.NewAPIKeyPool(keys)}","typeGuard":null,"tryCatchPattern":"result, err := provider.Search(ctx, q, n, rangeCode)\nif err != nil {\n    if err.Error() == \"no API key provided\" {\n        // config problem: disable brave or load keys, do not retry\n    }\n    return err\n}","preventionTips":["Fail fast at startup when the selected provider has no keys instead of at first search","Export provider keys in the actual service environment (systemd/docker), not just your shell","Prefer SearXNG for keyless deployments"],"tags":["web-search","api-key","brave","configuration"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}