can1357/oh-my-pi · info · MCPOAuthCancelledError

MCPOAuthCancelledError

Error message

MCPOAuthCancelledError

What it means

MCPOAuthCancelledError is thrown when the MCP OAuth flow ends because cancellation was explicitly requested — pressing Esc, an external AbortSignal firing, or a newer MCP flow superseding this one. The controller sets a cancellationRequested flag in those paths and converts the caught condition into this typed error so callers can distinguish a deliberate cancel from a real failure.

Source

Thrown at packages/coding-agent/src/modes/controllers/mcp-command-controller.ts:1025

				clientId: flow.resolvedClientId?.trim() || resolvedClientId,
				clientSecret: flow.registeredClientSecret ?? resolvedClientSecret,
				resource: flow.resource,
				authorizationUrl: flow.authorizationUrl,
			};

			await authStorage.set(credentialId, oauthCredential);

			return {
				credentialId,
				clientId: flow.resolvedClientId,
				resource: flow.resource,
			};
		} catch (error) {
			// Esc, an external abort, or a newer MCP flow are neutral
			// cancellations. The timeout path also aborts the controller but does
			// not set this flag, so it remains a surfaced error.
			if (cancellationRequested) {
				throw new MCPOAuthCancelledError();
			}

			const errorMsg = error instanceof Error ? error.message : String(error);

			// Provide helpful error messages based on failure type
			if (errorMsg.includes("timeout") || errorMsg.includes("timed out")) {
				throw new Error("OAuth flow timed out. Please try again.");
			} else if (errorMsg.includes("403") || errorMsg.includes("unauthorized")) {
				throw new Error("OAuth authorization failed. Please check your client credentials.");
			} else if (errorMsg.includes("invalid_grant")) {
				throw new Error("OAuth authorization code is invalid or expired. Please try again.");
			} else if (errorMsg.includes("ECONNREFUSED") || errorMsg.includes("fetch failed")) {
				throw new Error("Could not connect to OAuth server. Please check the URLs and your network connection.");
			} else {
				throw new Error(`OAuth authentication failed: ${errorMsg}`);
			}
		} finally {
			this.ctx.editor.onEscape = originalOnEscape;

View on GitHub (pinned to 9690622007)

Solutions

  1. No fix needed if the cancel was intentional — handle MCPOAuthCancelledError as a normal, non-error outcome and skip error reporting
  2. If unintended, avoid pressing Esc and check that no external signal is wired to abort the flow
  3. Re-run the OAuth flow to complete login

Example fix

// before
try { await startMcpOAuth(); } catch (e) { showError(e); }
// after
try { await startMcpOAuth(); }
catch (e) { if (e instanceof MCPOAuthCancelledError) return; showError(e); }
Defensive patterns

Strategy: try-catch

Type guard

function isMcpOAuthCancelled(e: unknown): e is MCPOAuthCancelledError {
  return e instanceof MCPOAuthCancelledError;
}

Try / catch

try {
  await runMcpOAuthFlow();
} catch (err) {
  if (isMcpOAuthCancelled(err)) {
    return; // intentional cancel — not an error
  }
  throw err;
}

Prevention

When it happens

Trigger: User presses Esc during the OAuth flow; an external AbortSignal passed to the flow aborts; a newer MCP OAuth flow starts and invalidates the current one.

Common situations: User changing their mind mid-login; a parent command or tool cancelling the flow via its signal; rapidly re-running the auth command so the new flow cancels the old.

Related errors


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