can1357/oh-my-pi · error

Invalid URL: ${input}

Error message

Invalid URL: ${input}

What it means

parseInternalUrl first attempts standard URL parsing; on failure it falls back to regex extraction. If neither the URL constructor nor the regex can find a host/scheme structure, the input is not a parseable internal URL and this error is thrown with the raw input echoed.

Source

Thrown at packages/coding-agent/src/internal-urls/parse.ts:63

}

/**
 * Parse an internal URL into an InternalUrl.
 *
 * Handles URLs where `new URL()` would fail (e.g., `skill://plugin:name`
 * where the colon is not a port separator).
 */
export function parseInternalUrl(input: string): InternalUrl {
	const hostMatch = input.match(SCHEME_HOST_RE);
	const pathMatch = input.match(PATHNAME_RE);

	let parsed: URL;
	try {
		parsed = new URL(input);
	} catch {
		// URL parse failed — build a minimal URL-like object from regex matches.
		if (!hostMatch) {
			throw new Error(`Invalid URL: ${input}`);
		}
		// Extract search and hash from the raw input before constructing the object.
		const hashIdx = input.indexOf("#");
		const hash = hashIdx !== -1 ? input.slice(hashIdx) : "";
		const withoutHash = hashIdx !== -1 ? input.slice(0, hashIdx) : input;
		const queryIdx = withoutHash.indexOf("?");
		const search = queryIdx !== -1 ? withoutHash.slice(queryIdx) : "";
		const queryString = search.slice(1); // strip leading ?

		// Strip search/hash from pathname captured by regex.
		let rawPathname = pathMatch?.[1] ?? "";
		if (queryIdx !== -1 && rawPathname.includes("?")) {
			rawPathname = rawPathname.slice(0, rawPathname.indexOf("?"));
		}

		parsed = {
			protocol: `${hostMatch[1]}:`,
			hostname: hostMatch[2] ?? "",

View on GitHub (pinned to 9690622007)

Solutions

  1. Ensure the input includes a supported scheme prefix, e.g. 'omp://docs' or 'memory://summary.md'.
  2. Trim whitespace and verify the value is a URL, not a filesystem path; use file:// for paths.
  3. Validate with a quick regex like /^[a-z][a-z0-9+.-]*:\/\//i before calling.

Example fix

// before
parseInternalUrl(userText);
// after
if (!/^[a-z][a-z0-9+.-]*:\/\//i.test(userText.trim())) throw new Error('expected scheme:// URL');
parseInternalUrl(userText.trim());
Defensive patterns

Strategy: validation

Validate before calling

const URL_LIKE = /^[a-z][a-z0-9+.-]*:\/\/\S+/i;
if (!URL_LIKE.test(input.trim())) throw new Error(`not an internal URL: ${input}`);

Type guard

const looksLikeUrl = (s: string): boolean => /^[a-z][a-z0-9+.-]*:\/\//i.test(s.trim());

Try / catch

try {
  return parseInternalUrl(input);
} catch (err) {
  if (err instanceof Error && err.message.startsWith('Invalid URL:')) throw new Error(`expected scheme:// URL, got: ${input}`);
  throw err;
}

Prevention

When it happens

Trigger: Calling parseInternalUrl (directly or via router.resolve/write) with a string lacking a recognizable scheme://host structure — e.g. a bare filename, empty string, whitespace, or garbage text.

Common situations: Passing a plain file path where a URL is expected; trimming/whitespace mistakes; tool output accidentally fed back as a URL.

Related errors


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