can1357/oh-my-pi · error · Error

collab.webUrl must start with http:// or https://

Error message

collab.webUrl must start with http:// or https://

What it means

normalizeCollabWebBaseUrl throws this when the explicitly configured webUrl cannot be parsed as a URL at all, or when its protocol is neither http: nor https:. The message is the same in both cases because a URL like 'collab.example.com' or 'wss://...' would fail here — the web base must be an HTTP(S) URL that browsers can open.

Source

Thrown at packages/coding-agent/src/collab/protocol.ts:221

		: normalized.origin;
	return `${compact}/r/${roomId}.${keyText}`;
}

function normalizeCollabWebBaseUrl(relayUrl: string, webUrl?: string): string {
	const explicitWebUrl = webUrl?.trim();
	if (!explicitWebUrl) {
		const normalized = normalizeRelayOrigin(relayUrl);
		if ("error" in normalized) throw new Error(normalized.error);
		return normalized.origin.startsWith("wss://")
			? `https://${normalized.origin.slice("wss://".length)}`
			: `http://${normalized.origin.slice("ws://".length)}`;
	}

	let url: URL;
	try {
		url = new URL(explicitWebUrl);
	} catch {
		throw new Error("collab.webUrl must start with http:// or https://");
	}
	if (url.protocol !== "http:" && url.protocol !== "https:") {
		throw new Error("collab.webUrl must start with http:// or https://");
	}
	if (url.protocol === "http:" && !isLocalHostname(url.hostname)) {
		throw new Error("collab.webUrl must use https:// unless it targets localhost");
	}
	if (url.search || url.hash) {
		throw new Error("collab.webUrl must not include a query string or fragment");
	}
	const path = url.pathname.replace(/\/+$/, "");
	return `${url.origin}${path}`;
}

/**
 * Render the browser deep link. The browser UI may be hosted separately from
 * the relay; the fragment always carries the relay-specific collab link, so
 * room secrets stay out of HTTP path and query bytes.

View on GitHub (pinned to 9690622007)

Solutions

  1. Set webUrl to a full URL with http:// or https:// scheme, e.g. https://collab.example.com.
  2. Do not reuse the relay's ws:// or wss:// URL as webUrl; derive the https address of the web UI instead.
  3. Trim the value and strip quotes before saving; verify with new URL(webUrl) in a quick script.
  4. Omit webUrl entirely to derive it from the relay origin.

Example fix

// before
webUrl: "collab.example.com"
// after
webUrl: "https://collab.example.com"
Defensive patterns

Strategy: validation

Validate before calling

function webUrlOk(s: string): boolean {
  try { const u = new URL(s.trim()); return u.protocol === "http:" || u.protocol === "https:"; }
  catch { return false; }
}
if (!webUrlOk(webUrl)) throw new Error("webUrl must be an absolute http(s) URL");
formatCollabWebLink(relayUrl, roomId, key, token, webUrl.trim());

Try / catch

try {
  const webLink = formatCollabWebLink(relayUrl, roomId, key, token, webUrl);
} catch (err) {
  ui.showError(`Invalid collab.webUrl: ${(err as Error).message}`);
}

Prevention

When it happens

Trigger: Calling formatCollabWebLink with webUrl set to: a scheme-less host ('collab.example.com'), a ws/wss URL, an ftp:// URL, or any string new URL() rejects (spaces, stray characters, empty after trim).

Common situations: Config collab.webUrl copied from the relay's wss:// address instead of the web UI's https:// address; webUrl set to a bare hostname; trailing whitespace or quotes left in a config value making new URL throw.

Related errors


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