can1357/oh-my-pi · error · Error
${normalized.error}
Error message
${normalized.error} What it means
formatCollabLink first normalizes the relay URL via normalizeRelayOrigin; if the URL is not a valid http(s) origin it returns an error object and formatCollabLink throws with that message instead of producing a link. This fails fast rather than emitting a link pointing at a broken relay.
Source
Thrown at packages/collab-web/src/lib/link.ts:130
}
/**
* Render the shareable link. Compact forms: the default relay collapses to
* `<roomId>.<key>`; custom wss relays drop the scheme (`host[:port]/r/…`);
* plain-ws localhost relays keep the full `ws://` URL.
*
* The room secret is dot-joined (`<roomId>.<key>`) rather than `#`-joined:
* RFC 3986 forbids a raw `#` inside a fragment, so strict URL stacks (macOS
* Foundation behind terminal click-to-open) percent-encode a second `#` to
* `%23` and break the link. Parsers still accept the legacy `#` form and the
* mangled `%23` form.
*
* Full links append the write token to the key
* (`base64url(key ∥ writeToken)`); read-only (view) links carry the bare key.
*/
export function formatCollabLink(relayUrl: string, roomId: string, key: Uint8Array, writeToken?: Uint8Array): string {
const normalized = normalizeRelayOrigin(relayUrl);
if ("error" in normalized) throw new Error(normalized.error);
let secret = key;
if (writeToken) {
secret = new Uint8Array(key.byteLength + writeToken.byteLength);
secret.set(key, 0);
secret.set(writeToken, key.byteLength);
}
const keyText = encodeBase64Url(secret);
if (normalized.origin === DEFAULT_RELAY_URL) return `${roomId}.${keyText}`;
const compact = normalized.origin.startsWith("wss://")
? normalized.origin.slice("wss://".length)
: normalized.origin;
return `${compact}/r/${roomId}.${keyText}`;
}
export function parseCollabLink(link: string): ParsedCollabLink | { error: string } {
// Lenient input: terminals that open OSC 8 links through strict URL stacks
// (macOS Foundation) percent-encode the legacy second `#` to `%23`.
let text = link.trim().replace(/%23/gi, "#");View on GitHub (pinned to 9690622007)
Solutions
- Pass a full origin URL including scheme (https:// or http://)
- Pre-normalize user-supplied relay settings: prepend https:// when no scheme is present
- Validate with normalizeRelayOrigin before calling and surface a friendly config error
- Check server config/env for empty or stale RELAY_URL values
Example fix
// before
const link = formatCollabLink(config.relayUrl, roomId, key); // throws if scheme missing
// after
const relay = config.relayUrl.startsWith("http") ? config.relayUrl : "https://" + config.relayUrl;
const link = formatCollabLink(relay, roomId, key); Defensive patterns
Strategy: validation
Validate before calling
function toRelayOrigin(url: string): string | null {
try {
const u = new URL(url);
return (u.protocol === "https:" || u.protocol === "http:") && u.pathname === "/" ? u.origin : null;
} catch {
return null;
}
} Try / catch
try {
const link = formatCollabLink(relayUrl, roomId, key);
} catch (e) {
throw new Error("Relay URL must be a full http(s) origin, got: " + relayUrl);
} Prevention
- Store relay config as full origin URLs with scheme
- Prepend https:// to scheme-less user input before use
- Trim whitespace from config values
- Validate relay settings at startup, not at link-generation time
When it happens
Trigger: Calling formatCollabLink with a relayUrl lacking a scheme (e.g. 'myrelay.example' instead of 'https://myrelay.example'), containing a path/query, or being an invalid/unparseable URL.
Common situations: Storing relay addresses in config without the https:// scheme, users typing 'localhost:8080' into a settings field, environment-specific relay URLs that are empty or contain whitespace.
Related errors
- Provider delete URL must not embed an account credential
- ${destination} returned an invalid upload URL
- Destination option endpoint must be an absolute URL
- Destination option endpoint must use HTTP or HTTPS
- Destination option ${optionName} must be an absolute HTTP UR
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/5c3eaf4637b788bd.
Report an issue: GitHub.