{"record":{"id":"c494b044a520754b","repo":"vxcontrol/pentagi","slug":"failed-to-create-google-search-service-v","errorCode":null,"errorMessage":"failed to create google search service: %v","messagePattern":"failed to create google search service: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/pkg/tools/searchers/google.go","lineNumber":132,"sourceCode":"\tclient, err := system.GetHTTPClient(g.cfg)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to create http client: %w\", err)\n\t}\n\n\t// google.golang.org/api normally injects the API key through the HTTP transport it\n\t// builds itself. But we MUST supply our own proxy/TLS client via WithHTTPClient, and\n\t// WithHTTPClient takes precedence — it replaces that transport, so option.WithAPIKey\n\t// is silently dropped and requests go out unauthenticated (HTTP 403 \"unregistered\n\t// caller\"). Attach the key ourselves as the `key` query parameter (the documented\n\t// Custom Search auth) by wrapping the proxy client's transport.\n\tclient.Transport = &googleAPIKeyTransport{\n\t\tkey:  g.apiKey(),\n\t\tbase: client.Transport,\n\t}\n\n\tsvc, err := customsearch.NewService(ctx, option.WithHTTPClient(client))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to create google search service: %v\", err)\n\t}\n\n\treturn svc, nil\n}\n\n// googleAPIKeyTransport attaches the Google API key as the `key` query parameter to\n// every outgoing request, so authentication survives the proxy/TLS client that\n// option.WithHTTPClient forces us to use (see newSearchService).\ntype googleAPIKeyTransport struct {\n\tkey  string\n\tbase http.RoundTripper\n}\n\nfunc (t *googleAPIKeyTransport) RoundTrip(req *http.Request) (*http.Response, error) {\n\t// Clone so the caller's request is never mutated.\n\tr := req.Clone(req.Context())\n\tq := r.URL.Query()\n\tq.Set(\"key\", t.key)","sourceCodeStart":114,"sourceCodeEnd":150,"githubUrl":"https://github.com/vxcontrol/pentagi/blob/ea665308baaff015b226f308438a68d929d0f29b/backend/pkg/tools/searchers/google.go#L114-L150","documentation":"newSearchService returns 'failed to create google search service: %v' when customsearch.NewService(ctx, option.WithHTTPClient(client)) fails to initialize the Custom Search service from the supplied HTTP client. Handle wraps this into a Fatal error, so the orchestrator falls through to other engines. Initialization rarely fails at runtime — it typically indicates context or client problems rather than Google-side issues.","triggerScenarios":"customsearch.NewService errors: the passed ctx is already canceled/deadline-exceeded, or the option.WithHTTPClient client is misconfigured (nil transport etc.). Note auth is handled manually via googleAPIKeyTransport, so NewService failure is not about the API key.","commonSituations":"Calling Handle with an expired request context; constructing the service per-request under tight timeouts instead of reusing it; odd proxy clients whose transport behaves unexpectedly during SDK init.","solutions":["Check whether the incoming ctx was canceled before Handle ran; fix upstream timeouts","Reuse a single customsearch.Service instance instead of rebuilding per query","Verify the client passed via option.WithHTTPClient has a valid non-nil Transport (googleAPIKeyTransport falls back to http.DefaultTransport if base is nil)","This is Fatal — no retry will help until the root cause is fixed"],"exampleFix":"// before: build service on every search\nsvc, err := customsearch.NewService(ctx, option.WithHTTPClient(client))\n// after: build once and reuse\nvar svcOnce sync.Once\nsvcOnce.Do(func() { svc, svcErr = customsearch.NewService(context.Background(), option.WithHTTPClient(client)) })","handlingStrategy":"fallback","validationCode":"// ensure the context is live before building the service\nif ctx.Err() != nil {\n    // skip: NewService will fail on a canceled context\n}","typeGuard":null,"tryCatchPattern":"svc, err := g.newSearchService(ctx)\nif err != nil {\n    return fallbackSearcher.Handle(ctx, req) // Fatal: retrying the same call won't help\n}","preventionTips":["Build the customsearch.Service once and reuse it (sync.Once or at construction time)","Never pass an already-canceled/short-deadline context into service construction","Keep option.WithHTTPClient's client valid with a non-nil Transport"],"tags":["google","customsearch","client-init","fatal"],"backgroundTag":"api-client-init-failed","analyzedSha":"ea665308baaff015b226f308438a68d929d0f29b","analyzedAt":"2026-09-01T14:16:31.421Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}