can1357/oh-my-pi · error · Error

OAuth resource URI must not include a fragment

Error message

OAuth resource URI must not include a fragment

What it means

RFC 8707 resource indicators must not contain a fragment; the resource is an audience identifier for the token, and a fragment would either break the provider's exact-match comparison or leak path data. resolveResourceUri parses the trimmed URL and rejects it if the URL has a hash fragment.

Source

Thrown at packages/coding-agent/src/mcp/oauth-flow.ts:256

		callbackHostname: resolveCallbackHostname(redirectUri),
		redirectUri,
		allowPortFallback,
	};
}

function resolveResourceUri(resource: string | undefined): string | undefined {
	const trimmed = resource?.trim();
	if (!trimmed) return undefined;
	if (trimmed !== resource) {
		throw new Error("OAuth resource URI must not include surrounding whitespace");
	}

	const parsed = new URL(trimmed);
	if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
		throw new Error("OAuth resource URI must use http or https");
	}
	if (parsed.hash) {
		throw new Error("OAuth resource URI must not include a fragment");
	}
	return trimmed;
}

interface ResourceIndicatorFilterOptions {
	/** Strip any resource URL on the same origin as the authorization server. */
	stripSameOriginResource?: boolean;
}

/**
 * Drop a redundant fallback resource indicator relative to {@link serverUrl}.
 *
 * Provider-advertised resource indicators are authoritative even when they are
 * origin-only (`https://gateway.example.com`) or path-scoped same-origin
 * (`https://gateway.example.com/my-service/mcp`): servers can use either form
 * as the audience they require for the grant.
 *
 * Plane is stricter for OMP-synthesized fallback resources (e.g. using the

View on GitHub (pinned to 9690622007)

Solutions

  1. Strip the #fragment from the configured resource URI
  2. Use only scheme://host/path?query form — typically just the origin or base path of the MCP server
  3. When copying URLs from a browser, remove any anchor portion before pasting into config

Example fix

// before
"resource": "https://acme.example/mcp#tools"
// after
"resource": "https://acme.example/mcp"
Defensive patterns

Strategy: validation

Validate before calling

const res = config.oauth?.resource;
if (res != null && new URL(res.trim()).hash) {
  throw new Error(`resource must not contain a fragment: '${res}'`);
}

Type guard

function isFragmentFreeUrl(s) {
  try { return !new URL(s).hash; } catch { return false; }
}

Try / catch

try {
  startOAuthFlow(config);
} catch (e) {
  if (e.message.includes('must not include a fragment')) {
    const u = new URL(config.oauth.resource);
    config.oauth.resource = u.origin + u.pathname;
    startOAuthFlow(config);
  } else throw e;
}

Prevention

When it happens

Trigger: Configuring the OAuth resource option with a trailing fragment, e.g. "https://acme.example/mcp#section" or a URL pasted from a browser that included an anchor, then constructing the flow options or accessing resolvedResource.

Common situations: Copying the MCP server URL straight from a browser address bar where a page anchor was present; template-generated URLs appending #fragment; confusion between resource indicators and web page URLs.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/724a066e0ef9d360. Report an issue: GitHub.