google-gemini/gemini-cli · error · OAuthSecurityError
Failed to resolve relative OAuth URL "${urlStr}" against bas
Error message
Failed to resolve relative OAuth URL "${urlStr}" against base "${options.baseUri}": ${getErrorMessage(e)} What it means
This error is thrown by validateOAuthEndpointUrl when a relative OAuth URL cannot be resolved against the provided baseUri using the URL constructor. It only occurs when options.allowRelative is true and options.baseUri is set, meaning the library was asked to accept relative endpoints but the combination of the relative string and the base produces an invalid URL. Typical causes are a malformed base URI (e.g. missing scheme like 'example.com/path') or a relative string that cannot be parsed even against the base.
Source
Thrown at packages/core/src/mcp/oauth-utils.ts:71
allowLoopback?: boolean;
expectedOrigin?: string;
allowRelative?: boolean;
baseUri?: string;
}
/**
* Validates an OAuth endpoint URL against SSRF, scheme, and origin constraints per RFC 9728 Section 7.7.
*/
export async function validateOAuthEndpointUrl(
urlStr: string,
options?: OAuthUrlValidationOptions,
): Promise<string> {
let resolvedUrl = urlStr.trim();
if (options?.allowRelative && options.baseUri) {
try {
resolvedUrl = new URL(resolvedUrl, options.baseUri).toString();
} catch (e) {
throw new OAuthSecurityError(
`Failed to resolve relative OAuth URL "${urlStr}" against base "${options.baseUri}": ${getErrorMessage(e)}`,
);
}
}
let parsed: URL;
try {
parsed = new URL(resolvedUrl);
} catch (e) {
throw new OAuthSecurityError(
`Invalid OAuth endpoint URL "${resolvedUrl}": ${getErrorMessage(e)}`,
);
}
const isHttp = parsed.protocol === 'http:';
const isHttps = parsed.protocol === 'https:';
if (!isHttp && !isHttps) {
throw new OAuthSecurityError(View on GitHub (pinned to 3c311beac2)
Solutions
- Verify options.baseUri is an absolute URL including scheme, e.g. 'https://server.example.com' (run new URL(baseUri) yourself to confirm it parses)
- Trim whitespace/quotes off both urlStr and baseUri before passing them
- If the endpoint is actually absolute, pass it as-is without allowRelative so resolution is skipped
- Log both urlStr and baseUri at the call site to identify which of the two is malformed
Example fix
// before
await validateOAuthEndpointUrl(meta.authorization_endpoint, { allowRelative: true, baseUri: serverUrl }); // serverUrl = 'localhost:3000'
// after
const base = serverUrl.startsWith('http') ? serverUrl : `https://${serverUrl}`;
await validateOAuthEndpointUrl(meta.authorization_endpoint, { allowRelative: true, baseUri: base }); Defensive patterns
Strategy: validation
Validate before calling
import { URL } from 'node:url';
function canResolveRelative(urlStr: string, baseUri: string): boolean {
try {
new URL(baseUri); // base must be absolute
new URL(urlStr.trim(), baseUri); // combination must parse
return true;
} catch {
return false;
}
} Type guard
function isAbsoluteHttpUrl(v: string): boolean {
try { const u = new URL(v); return u.protocol === 'http:' || u.protocol === 'https:'; }
catch { return false; }
} Try / catch
try {
await validateOAuthEndpointUrl(rel, { allowRelative: true, baseUri });
} catch (e) {
if (e instanceof OAuthSecurityError && e.message.startsWith('Failed to resolve relative OAuth URL')) {
// fix baseUri (ensure scheme) or pass an absolute endpoint, then retry
}
throw e;
} Prevention
- Normalize baseUri through new URL(base).toString() before passing it
- Validate OAuth config (endpoints, base URI) at startup rather than mid-flow
- Keep base URIs in one config module so scheme mistakes can't be introduced per call-site
When it happens
Trigger: Calling validateOAuthEndpointUrl('/authorize', { allowRelative: true, baseUri: 'not-a-valid-url' }) or passing a relative URL with unsupported syntax (e.g. '//host/path' against a base without a scheme), causing new URL(resolvedUrl, options.baseUri) to throw.
Common situations: Reading authorization_server or registration_endpoint fields from MCP resource metadata that contain relative paths (per RFC 8414) while the baseUri was stored without a protocol, contains stray whitespace/quotes, or was built from an env var that is empty or malformed.
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
- Invalid OAuth endpoint URL "${resolvedUrl}": ${getErrorMessa
- Invalid OAuth endpoint protocol "${parsed.protocol}". Only H
- Insecure HTTP OAuth endpoint "${resolvedUrl}" is not allowed
- Invalid expected origin "${options.expectedOrigin}".
- OAuth endpoint origin "${parsed.origin}" does not match expe
AI-assisted analysis of google-gemini/gemini-cli@3c311beac2 (2026-08-27).
Data as JSON: /api/errors/3f8e3c6a08e4378f.
Report an issue: GitHub.