{"record":{"id":"a08bbcc868c007db","repo":"can1357/oh-my-pi","slug":"mcpoauthcancellederror","errorCode":null,"errorMessage":"MCPOAuthCancelledError","messagePattern":"MCPOAuthCancelledError","errorType":"exception","errorClass":"MCPOAuthCancelledError","httpStatus":null,"severity":"info","filePath":"packages/coding-agent/src/modes/controllers/mcp-command-controller.ts","lineNumber":1025,"sourceCode":"\t\t\t\tclientId: flow.resolvedClientId?.trim() || resolvedClientId,\n\t\t\t\tclientSecret: flow.registeredClientSecret ?? resolvedClientSecret,\n\t\t\t\tresource: flow.resource,\n\t\t\t\tauthorizationUrl: flow.authorizationUrl,\n\t\t\t};\n\n\t\t\tawait authStorage.set(credentialId, oauthCredential);\n\n\t\t\treturn {\n\t\t\t\tcredentialId,\n\t\t\t\tclientId: flow.resolvedClientId,\n\t\t\t\tresource: flow.resource,\n\t\t\t};\n\t\t} catch (error) {\n\t\t\t// Esc, an external abort, or a newer MCP flow are neutral\n\t\t\t// cancellations. The timeout path also aborts the controller but does\n\t\t\t// not set this flag, so it remains a surfaced error.\n\t\t\tif (cancellationRequested) {\n\t\t\t\tthrow new MCPOAuthCancelledError();\n\t\t\t}\n\n\t\t\tconst errorMsg = error instanceof Error ? error.message : String(error);\n\n\t\t\t// Provide helpful error messages based on failure type\n\t\t\tif (errorMsg.includes(\"timeout\") || errorMsg.includes(\"timed out\")) {\n\t\t\t\tthrow new Error(\"OAuth flow timed out. Please try again.\");\n\t\t\t} else if (errorMsg.includes(\"403\") || errorMsg.includes(\"unauthorized\")) {\n\t\t\t\tthrow new Error(\"OAuth authorization failed. Please check your client credentials.\");\n\t\t\t} else if (errorMsg.includes(\"invalid_grant\")) {\n\t\t\t\tthrow new Error(\"OAuth authorization code is invalid or expired. Please try again.\");\n\t\t\t} else if (errorMsg.includes(\"ECONNREFUSED\") || errorMsg.includes(\"fetch failed\")) {\n\t\t\t\tthrow new Error(\"Could not connect to OAuth server. Please check the URLs and your network connection.\");\n\t\t\t} else {\n\t\t\t\tthrow new Error(`OAuth authentication failed: ${errorMsg}`);\n\t\t\t}\n\t\t} finally {\n\t\t\tthis.ctx.editor.onEscape = originalOnEscape;","sourceCodeStart":1007,"sourceCodeEnd":1043,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/modes/controllers/mcp-command-controller.ts#L1007-L1043","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\ntry { await startMcpOAuth(); } catch (e) { showError(e); }\n// after\ntry { await startMcpOAuth(); }\ncatch (e) { if (e instanceof MCPOAuthCancelledError) return; showError(e); }","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"function isMcpOAuthCancelled(e: unknown): e is MCPOAuthCancelledError {\n  return e instanceof MCPOAuthCancelledError;\n}","tryCatchPattern":"try {\n  await runMcpOAuthFlow();\n} catch (err) {\n  if (isMcpOAuthCancelled(err)) {\n    return; // intentional cancel — not an error\n  }\n  throw err;\n}","preventionTips":["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"],"tags":["oauth","mcp","cancellation"],"backgroundTag":"operation-cancelled","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}