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
- Update IssueProtocolHandler.resolve to handle the new parsed.kind variant — the error names the unexpected variant, so read it from the message.
- Check parseUrl in issue-pr-protocol.ts to see which variants it can return for the 'issue' scheme and confirm the union.
- Add an exhaustive switch (or a `satisfies never` check) so TypeScript flags a missing variant at compile time instead of this runtime throw.
- 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 extending the parsed-kind union, update both IssueProtocolHandler and PrProtocolHandler in the same change.
- Use an exhaustive switch with a `never` assignment so missing variants fail at compile time.
- Add a unit test per parsed kind exercising each handler.
- Treat occurrences at runtime as library bugs and report them with the URL.
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
- Unknown auth-broker action: ${String(_exhaustive)}
- Unknown auth-gateway action: ${String(_exhaustive)}
- pr://${repo}/${parsed.number}/diff/${index} resolved to a mi
- Unhandled stop reason: ${reason satisfies never}
- Destination option ${key} must be a string
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/7acd6d8600684bb6.
Report an issue: GitHub.