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
- No fix needed if the cancel was intentional — handle MCPOAuthCancelledError as a normal, non-error outcome and skip error reporting
- If unintended, avoid pressing Esc and check that no external signal is wired to abort the flow
- 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
- Treat MCPOAuthCancelledError as a normal control-flow outcome, not a failure
- Wire external AbortSignals deliberately — every listener is a potential cancel
- Suppress error toasts for cancellation errors in UI handlers
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
- OAuth refresh ownership aborted by caller
- Login cancelled
- MCP OAuth credential is missing refresh material
- Broker returned non-OAuth credential for ${provider}
- Token exchange returned no access token${providerError ? `:
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/a08bbcc868c007db.
Report an issue: GitHub.