google-gemini/gemini-cli · error · OAuthSecurityError
Invalid expected origin "${options.expectedOrigin}".
Error message
Invalid expected origin "${options.expectedOrigin}". What it means
The caller supplied options.expectedOrigin, but that value itself cannot be parsed as a URL, so its origin cannot be computed for comparison. This is a caller-configuration bug: the pinning origin is malformed before the endpoint is even compared against it.
Source
Thrown at packages/core/src/mcp/oauth-utils.ts:108
`Invalid OAuth endpoint protocol "${parsed.protocol}". Only HTTPS (and HTTP for local development) is supported.`,
);
}
const hostname = sanitizeHostname(parsed.hostname);
const isLoopback = isLoopbackHost(hostname);
if (isHttp && (!options?.allowLoopback || !isLoopback)) {
throw new OAuthSecurityError(
`Insecure HTTP OAuth endpoint "${resolvedUrl}" is not allowed. OAuth endpoints must use HTTPS unless connecting to localhost.`,
);
}
if (options?.expectedOrigin) {
let expected: string;
try {
expected = new URL(options.expectedOrigin).origin;
} catch {
throw new OAuthSecurityError(
`Invalid expected origin "${options.expectedOrigin}".`,
);
}
if (parsed.origin !== expected) {
throw new OAuthSecurityError(
`OAuth endpoint origin "${parsed.origin}" does not match expected origin "${expected}".`,
);
}
}
if (isLoopback) {
if (!options?.allowLoopback) {
throw new OAuthSecurityError(
`Loopback OAuth endpoint "${resolvedUrl}" is not allowed for remote MCP servers.`,
);
}
return parsed.toString();
}View on GitHub (pinned to 3c311beac2)
Solutions
- Set expectedOrigin to a clean origin string with scheme and host only, e.g. 'https://auth.example.com' (the code normalizes via URL.origin, but it must at least parse)
- Remove any path, query, or fragment from the configured origin
- Validate the config value at startup with new URL(origin) and fail fast with a clear config error
Example fix
// before
await validateOAuthEndpointUrl(url, { expectedOrigin: process.env.OAUTH_ORIGIN! }); // 'auth.example.com'
// after
await validateOAuthEndpointUrl(url, { expectedOrigin: 'https://auth.example.com' }); Defensive patterns
Strategy: validation
Validate before calling
function isParseableOrigin(v: string): boolean {
try { new URL(v); return true; } catch { return false; }
}
if (expectedOrigin && !isParseableOrigin(expectedOrigin)) {
throw new Error(`Config bug: expectedOrigin '${expectedOrigin}' is not a valid URL`);
} Type guard
function isValidOrigin(v: unknown): v is string {
return typeof v === 'string' && (() => { try { new URL(v); return true; } catch { return false; } })();
} Try / catch
try {
await validateOAuthEndpointUrl(url, { expectedOrigin });
} catch (e) {
if (e instanceof OAuthSecurityError && e.message.includes('Invalid expected origin')) {
// the expectedOrigin config itself is malformed; fix it (scheme + host, no path)
}
throw e;
} Prevention
- Store expectedOrigin as scheme+host only (https://auth.example.com)
- Validate it with new URL() at startup and fail fast
- Keep expectedOrigin in typed config, not ad-hoc strings at call sites
When it happens
Trigger: Calling validateOAuthEndpointUrl(url, { expectedOrigin: 'auth.example.com' }) (missing scheme), or with a path/query string ('https://host/path'), garbage string, or empty-ish value that fails new URL().
Common situations: Expected-origin values read from env vars or config files that lack the scheme, contain typos, or include path components; values copied from browser address bars with trailing slashes or paths.
Understand the failure class
Background: "Invalid URL" / "URL cannot be empty": fix the malformed or missing URL behind request-construction failures — this error's family across 50 libraries.
Related errors
- Failed to resolve relative OAuth URL "${urlStr}" against bas
- Invalid OAuth endpoint URL "${resolvedUrl}": ${getErrorMessa
- Invalid OAuth endpoint protocol "${parsed.protocol}". Only H
- OAuth endpoint origin "${parsed.origin}" does not match expe
- Invalid or unavailable mode: ${modeId}
AI-assisted analysis of google-gemini/gemini-cli@3c311beac2 (2026-08-27).
Data as JSON: /api/errors/5d1cac1e41a28731.
Report an issue: GitHub.