{"record":{"id":"d0d995e47fcb5b22","repo":"can1357/oh-my-pi","slug":"google-gemini-base-url-must-be-a-valid-absolute-ur","errorCode":null,"errorMessage":"GOOGLE_GEMINI_BASE_URL must be a valid absolute URL","messagePattern":"GOOGLE_GEMINI_BASE_URL must be a valid absolute URL","errorType":"validation","errorClass":"SearchProviderError","httpStatus":400,"severity":"error","filePath":"packages/coding-agent/src/web/search/providers/gemini.ts","lineNumber":56,"sourceCode":"\tif (envModel) return envModel;\n\tconst model = configuredModel?.trim();\n\treturn model || DEFAULT_MODEL;\n}\n\ninterface GeminiDeveloperEndpoint {\n\turl: string;\n\tauthProvider: typeof DEVELOPER_API_PROVIDER | typeof CLOUDFLARE_GATEWAY_PROVIDER;\n\tisCloudflareGateway: boolean;\n}\n\nfunction resolveGeminiDeveloperEndpoint(): GeminiDeveloperEndpoint {\n\tconst configuredHost = Bun.env.GOOGLE_GEMINI_BASE_URL?.trim().replace(/\\/+$/, \"\");\n\tconst host = configuredHost || DEFAULT_DEVELOPER_API_HOST;\n\tlet parsed: URL;\n\ttry {\n\t\tparsed = new URL(host);\n\t} catch {\n\t\tthrow new SearchProviderError(\"gemini\", \"GOOGLE_GEMINI_BASE_URL must be a valid absolute URL\", 400);\n\t}\n\tif (parsed.protocol !== \"https:\" && parsed.protocol !== \"http:\") {\n\t\tthrow new SearchProviderError(\"gemini\", \"GOOGLE_GEMINI_BASE_URL must use HTTP or HTTPS\", 400);\n\t}\n\tconst isCloudflareGateway = parsed.hostname === \"gateway.ai.cloudflare.com\";\n\treturn {\n\t\turl: `${host}/${DEVELOPER_API_VERSION}`,\n\t\tauthProvider: isCloudflareGateway ? CLOUDFLARE_GATEWAY_PROVIDER : DEVELOPER_API_PROVIDER,\n\t\tisCloudflareGateway,\n\t};\n}\n\nconst GEMINI_PROVIDERS = [\"google-gemini-cli\", \"google-antigravity\"] as const;\ntype GeminiProviderId = (typeof GEMINI_PROVIDERS)[number];\n\ninterface GeminiToolParams {\n\tgoogle_search?: Record<string, unknown>;\n\tcode_execution?: Record<string, unknown>;","sourceCodeStart":38,"sourceCodeEnd":74,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/web/search/providers/gemini.ts#L38-L74","documentation":"resolveGeminiDeveloperEndpoint() reads GOOGLE_GEMINI_BASE_URL (defaulting to the Gemini Developer API host) and parses it with `new URL()`. If the value is set but is not a parsable absolute URL — missing scheme entirely, or garbage text — it throws this SearchProviderError with status 400 before any network request is made. This guard also runs during isAvailable(), so it can surface at startup/availability checks, not only at search time.","triggerScenarios":"GOOGLE_GEMINI_BASE_URL set to a scheme-less value like 'generativelanguage.googleapis.com', a value with stray quotes/whitespace that fails URL parsing, or any placeholder/nonsense string. Triggered by endpoint() or isAvailable() when using the Gemini developer search provider.","commonSituations":"Users copying the host without the https:// prefix from docs, .env quoting mistakes, or CI injecting unset-marker placeholders into the variable.","solutions":["Set GOOGLE_GEMINI_BASE_URL to a full absolute URL including the scheme, e.g. https://generativelanguage.googleapis.com","If you use the default endpoint, unset the variable entirely so the built-in default host applies","Echo the variable (`printenv GOOGLE_GEMINI_BASE_URL | cat -A`) to spot hidden quotes/whitespace"],"exampleFix":"// before\nGOOGLE_GEMINI_BASE_URL=generativelanguage.googleapis.com\n// after\nGOOGLE_GEMINI_BASE_URL=https://generativelanguage.googleapis.com","handlingStrategy":"validation","validationCode":"const v = Bun.env.GOOGLE_GEMINI_BASE_URL?.trim();\nif (v) {\n  try {\n    new URL(v); // throws if not absolute\n  } catch {\n    throw new Error(\"GOOGLE_GEMINI_BASE_URL must be absolute, e.g. https://generativelanguage.googleapis.com\");\n  }\n}","typeGuard":"function isAbsoluteUrl(value: string | undefined): boolean {\n  if (!value) return true; // unset uses default\n  try { new URL(value.trim()); return true; } catch { return false; }\n}","tryCatchPattern":"try {\n  results = await searchGemini(query);\n} catch (err) {\n  if (err instanceof SearchProviderError && err.message.includes(\"must be a valid absolute URL\")) {\n    logger.error(\"Fix GOOGLE_GEMINI_BASE_URL: include the https:// scheme\", { cause: err.message });\n    results = await fallbackSearch(query);\n  } else throw err;\n}","preventionTips":["Always prefix custom base URLs with https:// — bare hostnames are rejected","Validate GOOGLE_GEMINI_BASE_URL during config load / startup, before first search","Check for stray quotes or whitespace in .env entries with `printenv | cat -A`","Leave the variable unset if you want the default Google endpoint"],"tags":["configuration","env-var","url-validation"],"backgroundTag":"invalid-base-url","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}