{"record":{"id":"ab4d09c017d7a595","repo":"Tencent/WeKnora","slug":"api-key-is-required-for-exa-provider-ab4d09","errorCode":null,"errorMessage":"API key is required for Exa provider","messagePattern":"API key is required for Exa provider","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/infrastructure/web_search/exa.go","lineNumber":41,"sourceCode":"\tdefaultExaResults   = 5\n\tmaxExaResults       = 100\n\tmaxExaResponseBytes = 2 << 20\n\tmaxExaContentRunes  = 12000\n)\n\n// ExaProvider implements web search using Exa's official Search API.\ntype ExaProvider struct {\n\tclient      *http.Client\n\tbaseURL     string\n\tapiKey      string\n\tincludeText bool\n}\n\n// NewExaProvider creates an Exa provider from tenant-specific parameters.\nfunc NewExaProvider(params types.WebSearchProviderParameters) (interfaces.WebSearchProvider, error) {\n\tapiKey := strings.TrimSpace(params.APIKey)\n\tif apiKey == \"\" {\n\t\treturn nil, fmt.Errorf(\"API key is required for Exa provider\")\n\t}\n\tclient, err := NewSearchHTTPClient(defaultExaTimeout, params.ProxyURL)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &ExaProvider{\n\t\tclient:      client,\n\t\tbaseURL:     defaultExaSearchURL,\n\t\tapiKey:      apiKey,\n\t\tincludeText: parseExaBool(params.ExtraConfig, \"include_text\"),\n\t}, nil\n}\n\n// Name returns the provider type identifier.\nfunc (p *ExaProvider) Name() string { return \"exa\" }\n\n// Search performs a web search through Exa's official Search API.\nfunc (p *ExaProvider) Search(","sourceCodeStart":23,"sourceCodeEnd":59,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/infrastructure/web_search/exa.go#L23-L59","documentation":"NewExaProvider validates that the tenant-supplied WebSearchProviderParameters contain a non-empty API key; if the trimmed key is empty it refuses to construct the provider. This is an intentional configuration guard so misconfigured tenants fail fast at construction rather than at request time with a 401.","triggerScenarios":"Calling NewExaProvider with params.APIKey empty or whitespace-only — e.g. the tenant web-search config was saved without a key, or the env var backing it was unset.","commonSituations":"Missing EXA_API_KEY in deployment env, config migration dropped the apiKey field, key stored with only whitespace, wiring the Exa provider while only a Tavily/DuckDuckGo key exists.","solutions":["Set the Exa API key in the tenant web-search provider parameters","Ensure the backing environment variable/secret (e.g. EXA_API_KEY) is present in the deployment","Trim and re-save the config if the key was stored with stray whitespace","Fall back to or select a different configured search provider if no Exa key is available"],"exampleFix":"// before\nprovider, err := NewExaProvider(types.WebSearchProviderParameters{})\n// after\nif os.Getenv(\"EXA_API_KEY\") == \"\" {\n    log.Fatal(\"EXA_API_KEY not set\")\n}\nprovider, err := NewExaProvider(types.WebSearchProviderParameters{APIKey: os.Getenv(\"EXA_API_KEY\")})","handlingStrategy":"validation","validationCode":"func validateExaParams(p types.WebSearchProviderParameters) error {\n    if strings.TrimSpace(p.APIKey) == \"\" {\n        return errors.New(\"API key is required for Exa provider\")\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":"provider, err := NewExaProvider(params)\nif err != nil {\n    if strings.Contains(err.Error(), \"API key is required\") {\n        return configureFallbackProvider() // e.g. DuckDuckGo/Tavily\n    }\n    return err\n}","preventionTips":["Require the Exa API key via env var or secret manager, never hardcode","Validate provider parameters at config load/startup time","Trim stored keys and reject whitespace-only values","Health-check provider construction at boot and surface missing-key errors early"],"tags":["config","api-key","validation","go"],"backgroundTag":"missing-api-key","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}