{"record":{"id":"2f5b24df0a26b703","repo":"vxcontrol/pentagi","slug":"internal-engine-no-link-discovery-engine-is-avail","errorCode":null,"errorMessage":"internal engine: no link-discovery engine is available","messagePattern":"internal engine: no link-discovery engine is available","errorType":"exception","errorClass":"FatalError","httpStatus":null,"severity":"error","filePath":"backend/pkg/tools/searchers/internal.go","lineNumber":223,"sourceCode":"// discoverURLs asks the first available link searcher for candidate URLs and extracts\n// them from its markdown output. A discovery error is propagated as-is (already typed).\nfunc (e *internalEngine) discoverURLs(ctx context.Context, req Request) ([]string, string, error) {\n\tlinkReq := Request{Query: req.Query, MaxResults: internalLinkDiscoveryLimit}\n\n\tfor _, l := range e.links {\n\t\tif l == nil || !l.IsAvailable() {\n\t\t\tcontinue\n\t\t}\n\t\tout, err := l.Handle(ctx, linkReq)\n\t\tif err != nil {\n\t\t\t// Propagate the typed error from the link engine; the orchestrator decides\n\t\t\t// retry-vs-fallback for the internal engine as a whole.\n\t\t\treturn nil, \"\", err\n\t\t}\n\t\treturn dedupeURLs(urlPattern.FindAllString(out, -1)), out, nil\n\t}\n\n\treturn nil, \"\", Fatal(fmt.Errorf(\"internal engine: no link-discovery engine is available\"))\n}\n\nfunc (e *internalEngine) buildPrompt(query string, blocks []string) string {\n\tvar sb strings.Builder\n\tsb.WriteString(\"<instructions>\\n\")\n\tsb.WriteString(\"TASK: Answer the user query using ONLY the web sources below.\\n\\n\")\n\tsb.WriteString(fmt.Sprintf(\"USER QUERY: %q\\n\\n\", query))\n\tsb.WriteString(\"REQUIREMENTS:\\n\")\n\tsb.WriteString(\"1. Give a direct, comprehensive answer to the user query.\\n\")\n\tsb.WriteString(\"2. Preserve critical facts, numbers, commands, code snippets, and technical details.\\n\")\n\tsb.WriteString(\"3. Remove any non-essential details that are not relevant to the user query.\\n\")\n\tsb.WriteString(\"4. Cite sources as [Source #] where # is the source id.\\n\")\n\tsb.WriteString(\"5. If the sources do not answer the query, say so explicitly.\\n\")\n\tsb.WriteString(\"6. Use `###` headers to separate paragraphs.\\n\")\n\tsb.WriteString(\"</instructions>\\n\\n\")\n\tfor _, b := range blocks {\n\t\tsb.WriteString(b)\n\t\tsb.WriteString(\"\\n\\n\")","sourceCodeStart":205,"sourceCodeEnd":241,"githubUrl":"https://github.com/vxcontrol/pentagi/blob/ea665308baaff015b226f308438a68d929d0f29b/backend/pkg/tools/searchers/internal.go#L205-L241","documentation":"`discoverURLs` iterates over the internal engine's configured link-discovery searchers and calls the first one whose `IsAvailable()` is true. If every entry is nil or unavailable (no search backend configured/keys missing), it returns this Fatal error (backend/pkg/tools/searchers/internal.go:223) — the internal engine cannot even begin because it depends entirely on a link-discovery engine for candidate URLs.","triggerScenarios":"`Handle` on the internal engine runs while no link searcher is configured, or all configured ones return `IsAvailable() == false` because their API keys/env vars (e.g. DUCKDUCKGO_*, SEARCHXNG_URL, TAVILY_API_KEY) are unset.","commonSituations":"Fresh deployment with no search-engine keys in .env; search engine disabled in Settings UI after enabling the internal engine; typo'd env var so availability check fails; `links` slice built empty in buildSearchEngines because all constructors reported not-configured.","solutions":["Configure at least one link-capable search engine: set its API key/env vars in .env (and docker-compose.yml) and restart","Check Settings UI → search engines and enable one whose Engine() can serve as a link source","Verify the engine is placed in the internal engine's link list in buildSearchEngines (internal.go constructor wiring)","Confirm env vars are actually passed into the backend container (docker compose config) — a missing env var makes IsAvailable() false"],"exampleFix":"// before: .env with no search engine\n# no search keys\n// after\nSEARXNG_URL=http://searxng:8080\n# or\nTAVILY_API_KEY=tvly-...","handlingStrategy":"validation","validationCode":"// startup check: at least one link engine available\navail := false\nfor _, l := range linkEngines {\n    if l != nil && l.IsAvailable() { avail = true; break }\n}\nif !avail {\n    return fmt.Errorf(\"no link-discovery search engine configured; set e.g. SEARXNG_URL or TAVILY_API_KEY\")\n}","typeGuard":null,"tryCatchPattern":"_, err := engine.Handle(ctx, req)\nif err != nil && searchers.IsFatal(err) && strings.Contains(err.Error(), \"no link-discovery engine\") {\n    // configuration problem: do not retry, fix env/config\n}","preventionTips":["Set at least one search-engine API key/env var in .env and docker-compose.yml before enabling the internal engine","Verify env vars reach the backend container (docker compose config)","Enable search engines in the Settings UI and confirm IsAvailable() passes at startup","Keep engine wiring in buildSearchEngines consistent with enabled config"],"tags":["configuration","missing-env-var","search"],"backgroundTag":"no-providers-configured","analyzedSha":"ea665308baaff015b226f308438a68d929d0f29b","analyzedAt":"2026-09-01T14:16:31.421Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}