can1357/oh-my-pi · error

Invalid issue:// URL: unexpected variant '${parsed.kind}'

Error message

Invalid issue:// URL: unexpected variant '${parsed.kind}'

What it means

Defensive exhaustiveness guard in IssueProtocolHandler.resolve (packages/coding-agent/src/internal-urls/issue-pr-protocol.ts:532). parseUrl already rejects issue://.../diff URLs and the union of parsed kinds is expected to be 'list' | 'single'; if any other variant appears, TypeScript's narrowing has failed (union grew without the handler being updated). This throw is a belt-and-suspenders catch, not a user-facing validation.

Source

Thrown at packages/coding-agent/src/internal-urls/issue-pr-protocol.ts:532

	readonly immutable = true;

	async resolve(url: InternalUrl, context?: ResolveContext): Promise<InternalResource> {
		if (context?.signal?.aborted) {
			throw new Error("aborted");
		}
		const parsed = parseUrl(url, "issue");
		if (parsed.kind === "list") {
			try {
				return await fetchAndRenderList("issue", parsed, url, context);
			} catch (err) {
				const message = err instanceof Error ? err.message : String(err);
				throw new Error(`issue:// listing failed: ${message}`);
			}
		}
		// parseUrl already rejects `issue://.../diff`; this guard is a belt-and-
		// suspenders catch in case the union grows.
		if (parsed.kind !== "single") {
			throw new Error(`Invalid issue:// URL: unexpected variant '${parsed.kind}'`);
		}
		try {
			const lookup = await getOrFetchIssue({
				cwd: resolveCwd(context),
				repo: parsed.repo,
				issue: String(parsed.number),
				includeComments: parsed.comments,
				signal: context?.signal,
				settings: settingsFromContext(context),
			});
			return buildSingleResource({
				url,
				scheme: "issue",
				parsed,
				rendered: lookup.rendered,
				status: lookup.status,
				fetchedAt: lookup.fetchedAt,
			});

View on GitHub (pinned to 9690622007)

Solutions

  1. Update IssueProtocolHandler.resolve to handle the new parsed.kind variant — the error names the unexpected variant, so read it from the message.
  2. Check parseUrl in issue-pr-protocol.ts to see which variants it can return for the 'issue' scheme and confirm the union.
  3. Add an exhaustive switch (or a `satisfies never` check) so TypeScript flags a missing variant at compile time instead of this runtime throw.
  4. If you hit this as a user, it is a library bug — report it with the URL that triggered it.

Example fix

// before
if (parsed.kind !== "single") {
  throw new Error(`Invalid issue:// URL: unexpected variant '${parsed.kind}'`);
}
// after
switch (parsed.kind) {
  case "single": break;
  case "issue-diff": /* handle new variant */ break;
  default: {
    const _exhaustive: never = parsed;
    throw new Error(`Invalid issue:// URL: unexpected variant '${String(_exhaustive)}'`);
  }
}
Defensive patterns

Strategy: type-guard

Validate before calling

null

Type guard

function isKnownIssueKind(parsed: ParsedUrl): parsed is Extract<ParsedUrl, { kind: "list" | "single" }> {
  return parsed.kind === "list" || parsed.kind === "single";
}

Try / catch

try {
  return await issueHandler.resolve(url, ctx);
} catch (err) {
  if (/Invalid issue:\/\/ URL: unexpected variant/.test(String(err.message))) {
    throw new Error(`Library bug in issue-pr-protocol parseUrl/handler union: ${err instanceof Error ? err.message : err}`);
  }
  throw err;
}

Prevention

When it happens

Trigger: Only reachable if ParsedUrl union for the 'issue' scheme gains a new kind (e.g. a future issue-diff variant) and IssueProtocolHandler.resolve is not updated, or if parseUrl's narrowing/discrimination is broken by a refactor.

Common situations: Maintainer adds a new parsed-kind to the shared parseUrl union and only updates PrProtocolHandler; type-level exhaustiveness wasn't enforced so the new kind silently reaches this guard at runtime.

Related errors


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